mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-22 07:07:30 +09:00
[network] Improve thread-safety of cache entry handling.
This commit is contained in:
parent
2073c44990
commit
9299c2e2ea
8 changed files with 141 additions and 105 deletions
|
|
@ -143,8 +143,11 @@ CacheEntry::Callback::~Callback()
|
||||||
MOZ_COUNT_DTOR(CacheEntry::Callback);
|
MOZ_COUNT_DTOR(CacheEntry::Callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have locks on both this and aEntry
|
||||||
void CacheEntry::Callback::ExchangeEntry(CacheEntry* aEntry)
|
void CacheEntry::Callback::ExchangeEntry(CacheEntry* aEntry)
|
||||||
{
|
{
|
||||||
|
aEntry->mLock.AssertCurrentThreadOwns();
|
||||||
|
mEntry->mLock.AssertCurrentThreadOwns();
|
||||||
if (mEntry == aEntry)
|
if (mEntry == aEntry)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -209,11 +212,11 @@ CacheEntry::CacheEntry(const nsACString& aStorageID,
|
||||||
, mStorageID(aStorageID)
|
, mStorageID(aStorageID)
|
||||||
, mUseDisk(aUseDisk)
|
, mUseDisk(aUseDisk)
|
||||||
, mSkipSizeCheck(aSkipSizeCheck)
|
, mSkipSizeCheck(aSkipSizeCheck)
|
||||||
|
, mPinned(aPin)
|
||||||
, mIsDoomed(false)
|
, mIsDoomed(false)
|
||||||
, mSecurityInfoLoaded(false)
|
, mSecurityInfoLoaded(false)
|
||||||
, mPreventCallbacks(false)
|
, mPreventCallbacks(false)
|
||||||
, mHasData(false)
|
, mHasData(false)
|
||||||
, mPinned(aPin)
|
|
||||||
, mPinningKnown(false)
|
, mPinningKnown(false)
|
||||||
, mState(NOTLOADED)
|
, mState(NOTLOADED)
|
||||||
, mRegistration(NEVERREGISTERED)
|
, mRegistration(NEVERREGISTERED)
|
||||||
|
|
@ -297,9 +300,6 @@ nsresult CacheEntry::HashingKey(nsCSubstring const& aStorageID,
|
||||||
|
|
||||||
void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags)
|
void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags)
|
||||||
{
|
{
|
||||||
LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
|
|
||||||
this, StateString(mState), aFlags, aCallback));
|
|
||||||
|
|
||||||
bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
|
bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
|
||||||
bool bypassIfBusy = aFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY;
|
bool bypassIfBusy = aFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY;
|
||||||
bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
|
bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
|
||||||
|
|
@ -307,8 +307,20 @@ void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags
|
||||||
bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED;
|
bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED;
|
||||||
bool secret = aFlags & nsICacheStorage::OPEN_SECRETLY;
|
bool secret = aFlags & nsICacheStorage::OPEN_SECRETLY;
|
||||||
|
|
||||||
MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
|
if (MOZ_LOG_TEST(gCache2Log, LogLevel::Debug)) {
|
||||||
MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry");
|
MutexAutoLock lock(mLock);
|
||||||
|
LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
|
||||||
|
this, StateString(mState), aFlags, aCallback));
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
{
|
||||||
|
// yes, if logging is on in DEBUG we'll take the lock twice in a row
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
|
||||||
|
MOZ_ASSERT(!(truncate && mState > LOADING),
|
||||||
|
"Must not call truncate on already loaded entry");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Callback callback(this, aCallback, readonly, multithread, secret);
|
Callback callback(this, aCallback, readonly, multithread, secret);
|
||||||
|
|
||||||
|
|
@ -470,7 +482,7 @@ NS_IMETHODIMP CacheEntry::OnFileReady(nsresult aResult, bool aIsNew)
|
||||||
|
|
||||||
mPinned = mFile->IsPinned();;
|
mPinned = mFile->IsPinned();;
|
||||||
mPinningKnown = true;
|
mPinningKnown = true;
|
||||||
LOG((" pinning=%d", mPinned));
|
LOG((" pinning=%d", (bool)mPinned));
|
||||||
|
|
||||||
if (mState == READY) {
|
if (mState == READY) {
|
||||||
mHasData = true;
|
mHasData = true;
|
||||||
|
|
@ -560,6 +572,7 @@ already_AddRefed<CacheEntryHandle> CacheEntry::ReopenTruncated(bool aMemoryOnly,
|
||||||
void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry)
|
void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry)
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
aFromEntry.mLock.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
|
LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
|
||||||
this, &aFromEntry));
|
this, &aFromEntry));
|
||||||
|
|
@ -684,11 +697,10 @@ bool CacheEntry::InvokeCallbacks(bool aReadOnly)
|
||||||
|
|
||||||
bool CacheEntry::InvokeCallback(Callback & aCallback)
|
bool CacheEntry::InvokeCallback(Callback & aCallback)
|
||||||
{
|
{
|
||||||
|
mLock.AssertCurrentThreadOwns();
|
||||||
LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
|
LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
|
||||||
this, StateString(mState), aCallback.mCallback.get()));
|
this, StateString(mState), aCallback.mCallback.get()));
|
||||||
|
|
||||||
mLock.AssertCurrentThreadOwns();
|
|
||||||
|
|
||||||
// When this entry is doomed we want to notify the callback any time
|
// When this entry is doomed we want to notify the callback any time
|
||||||
if (!mIsDoomed) {
|
if (!mIsDoomed) {
|
||||||
// When we are here, the entry must be loaded from disk
|
// When we are here, the entry must be loaded from disk
|
||||||
|
|
@ -802,15 +814,21 @@ bool CacheEntry::InvokeCallback(Callback & aCallback)
|
||||||
|
|
||||||
void CacheEntry::InvokeAvailableCallback(Callback const & aCallback)
|
void CacheEntry::InvokeAvailableCallback(Callback const & aCallback)
|
||||||
{
|
{
|
||||||
LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
|
|
||||||
this, StateString(mState), aCallback.mCallback.get(), aCallback.mReadOnly, aCallback.mNotWanted));
|
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
uint32_t state;
|
||||||
|
{ // Scope for lock
|
||||||
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
state = mState;
|
||||||
|
LOG(
|
||||||
|
("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, "
|
||||||
|
"r/o=%d, "
|
||||||
|
"n/w=%d]",
|
||||||
|
this, StateString(mState), aCallback.mCallback.get(),
|
||||||
|
aCallback.mReadOnly, aCallback.mNotWanted));
|
||||||
|
|
||||||
uint32_t const state = mState;
|
// When we are here, the entry must be loaded from disk
|
||||||
|
MOZ_ASSERT(state > LOADING || mIsDoomed);
|
||||||
// When we are here, the entry must be loaded from disk
|
}
|
||||||
MOZ_ASSERT(state > LOADING || mIsDoomed);
|
|
||||||
|
|
||||||
bool onAvailThread;
|
bool onAvailThread;
|
||||||
rv = aCallback.OnAvailThread(&onAvailThread);
|
rv = aCallback.OnAvailThread(&onAvailThread);
|
||||||
|
|
@ -911,11 +929,10 @@ CacheEntryHandle* CacheEntry::NewWriteHandle()
|
||||||
|
|
||||||
void CacheEntry::OnHandleClosed(CacheEntryHandle const* aHandle)
|
void CacheEntry::OnHandleClosed(CacheEntryHandle const* aHandle)
|
||||||
{
|
{
|
||||||
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState), aHandle));
|
LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState), aHandle));
|
||||||
|
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
if (mIsDoomed && NS_SUCCEEDED(mFileStatus) &&
|
||||||
|
|
||||||
if (IsDoomed() && NS_SUCCEEDED(mFileStatus) &&
|
|
||||||
// Note: mHandlesCount is dropped before this method is called
|
// Note: mHandlesCount is dropped before this method is called
|
||||||
(mHandlesCount == 0 ||
|
(mHandlesCount == 0 ||
|
||||||
(mHandlesCount == 1 && mWriter && mWriter != aHandle))
|
(mHandlesCount == 1 && mWriter && mWriter != aHandle))
|
||||||
|
|
@ -1064,8 +1081,12 @@ NS_IMETHODIMP CacheEntry::GetIsForcedValid(bool *aIsForcedValid)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG(aIsForcedValid);
|
NS_ENSURE_ARG(aIsForcedValid);
|
||||||
|
|
||||||
MOZ_ASSERT(mState > LOADING);
|
#ifdef DEBUG
|
||||||
|
{
|
||||||
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
MOZ_ASSERT(mState > LOADING);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (mPinned) {
|
if (mPinned) {
|
||||||
*aIsForcedValid = true;
|
*aIsForcedValid = true;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
@ -1429,12 +1450,11 @@ NS_IMETHODIMP CacheEntry::MetaDataReady()
|
||||||
|
|
||||||
NS_IMETHODIMP CacheEntry::SetValid()
|
NS_IMETHODIMP CacheEntry::SetValid()
|
||||||
{
|
{
|
||||||
LOG(("CacheEntry::SetValid [this=%p, state=%s]", this, StateString(mState)));
|
|
||||||
|
|
||||||
nsCOMPtr<nsIOutputStream> outputStream;
|
nsCOMPtr<nsIOutputStream> outputStream;
|
||||||
|
|
||||||
{
|
{ // Scope for lock
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
LOG(("CacheEntry::SetValid [this=%p, state=%s]", this, StateString(mState)));
|
||||||
|
|
||||||
MOZ_ASSERT(mState > EMPTY);
|
MOZ_ASSERT(mState > EMPTY);
|
||||||
|
|
||||||
|
|
@ -1457,9 +1477,8 @@ NS_IMETHODIMP CacheEntry::SetValid()
|
||||||
NS_IMETHODIMP CacheEntry::Recreate(bool aMemoryOnly,
|
NS_IMETHODIMP CacheEntry::Recreate(bool aMemoryOnly,
|
||||||
nsICacheEntry **_retval)
|
nsICacheEntry **_retval)
|
||||||
{
|
{
|
||||||
LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState)));
|
|
||||||
|
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState)));
|
||||||
|
|
||||||
RefPtr<CacheEntryHandle> handle = ReopenTruncated(aMemoryOnly, nullptr);
|
RefPtr<CacheEntryHandle> handle = ReopenTruncated(aMemoryOnly, nullptr);
|
||||||
if (handle) {
|
if (handle) {
|
||||||
|
|
@ -1600,7 +1619,7 @@ bool CacheEntry::DeferOrBypassRemovalOnPinStatus(bool aPinned)
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
|
||||||
if (mPinningKnown) {
|
if (mPinningKnown) {
|
||||||
LOG((" pinned=%d, caller=%d", mPinned, aPinned));
|
LOG((" pinned=%d, caller=%d", (bool)mPinned, aPinned));
|
||||||
// Bypass when the pin status of this entry doesn't match the pin status
|
// Bypass when the pin status of this entry doesn't match the pin status
|
||||||
// caller wants to remove
|
// caller wants to remove
|
||||||
return mPinned != aPinned;
|
return mPinned != aPinned;
|
||||||
|
|
@ -1873,10 +1892,12 @@ NS_IMETHODIMP CacheOutputCloseListener::Run()
|
||||||
|
|
||||||
// Memory reporting
|
// Memory reporting
|
||||||
|
|
||||||
size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
|
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
|
||||||
n += mCallbacks.ShallowSizeOfExcludingThis(mallocSizeOf);
|
n += mCallbacks.ShallowSizeOfExcludingThis(mallocSizeOf);
|
||||||
if (mFile) {
|
if (mFile) {
|
||||||
n += mFile->SizeOfIncludingThis(mallocSizeOf);
|
n += mFile->SizeOfIncludingThis(mallocSizeOf);
|
||||||
|
|
@ -1896,7 +1917,7 @@ size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CacheEntry::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
size_t CacheEntry::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||||
{
|
{
|
||||||
return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
|
return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,8 @@ public:
|
||||||
::mozilla::Atomic<uint32_t, ::mozilla::Relaxed> mSortingExpirationTime;
|
::mozilla::Atomic<uint32_t, ::mozilla::Relaxed> mSortingExpirationTime;
|
||||||
|
|
||||||
// Memory reporting
|
// Memory reporting
|
||||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~CacheEntry();
|
virtual ~CacheEntry();
|
||||||
|
|
@ -152,7 +152,8 @@ private:
|
||||||
void ExchangeEntry(CacheEntry* aEntry);
|
void ExchangeEntry(CacheEntry* aEntry);
|
||||||
|
|
||||||
// Returns true when an entry is about to be "defer" doomed and this is
|
// Returns true when an entry is about to be "defer" doomed and this is
|
||||||
// a "defer" callback.
|
// a "defer" callback. The caller must hold a lock (this entry is in the
|
||||||
|
// caller's mCallback array)
|
||||||
bool DeferDoom(bool *aDoom) const;
|
bool DeferDoom(bool *aDoom) const;
|
||||||
|
|
||||||
// We are raising reference count here to take into account the pending
|
// We are raising reference count here to take into account the pending
|
||||||
|
|
@ -289,9 +290,10 @@ private:
|
||||||
// When mFileStatus is read and found success it is ensured there is mFile and
|
// When mFileStatus is read and found success it is ensured there is mFile and
|
||||||
// that it is after a successful call to Init().
|
// that it is after a successful call to Init().
|
||||||
::mozilla::Atomic<nsresult, ::mozilla::ReleaseAcquire> mFileStatus;
|
::mozilla::Atomic<nsresult, ::mozilla::ReleaseAcquire> mFileStatus;
|
||||||
nsCString mURI;
|
// Set in constructor
|
||||||
nsCString mEnhanceID;
|
nsCString const mURI;
|
||||||
nsCString mStorageID;
|
nsCString const mEnhanceID;
|
||||||
|
nsCString const mStorageID;
|
||||||
|
|
||||||
// mUseDisk, mSkipSizeCheck, mIsDoomed are plain "bool", not "bool:1",
|
// mUseDisk, mSkipSizeCheck, mIsDoomed are plain "bool", not "bool:1",
|
||||||
// so as to avoid bitfield races with the byte containing
|
// so as to avoid bitfield races with the byte containing
|
||||||
|
|
@ -302,7 +304,9 @@ private:
|
||||||
// Whether it should skip max size check.
|
// Whether it should skip max size check.
|
||||||
bool const mSkipSizeCheck;
|
bool const mSkipSizeCheck;
|
||||||
// Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
|
// Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
|
||||||
bool mIsDoomed;
|
Atomic<bool, Relaxed> mIsDoomed;
|
||||||
|
// The indication of pinning this entry was open with
|
||||||
|
Atomic<bool, Relaxed> mPinned;
|
||||||
|
|
||||||
// Following flags are all synchronized with the cache entry lock.
|
// Following flags are all synchronized with the cache entry lock.
|
||||||
|
|
||||||
|
|
@ -317,8 +321,6 @@ private:
|
||||||
// false: after load and a new file, or dropped to back to false when a writer
|
// false: after load and a new file, or dropped to back to false when a writer
|
||||||
// fails to open an output stream.
|
// fails to open an output stream.
|
||||||
bool mHasData : 1;
|
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
|
// Whether the pinning state of the entry is known (equals to the actual state
|
||||||
// of the cache file)
|
// of the cache file)
|
||||||
bool mPinningKnown : 1;
|
bool mPinningKnown : 1;
|
||||||
|
|
|
||||||
|
|
@ -323,6 +323,16 @@ CacheFile::Init(const nsACString &aKey,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CacheFile::Key(nsACString& aKey) {
|
||||||
|
CacheFileAutoLock lock(this);
|
||||||
|
aKey = mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CacheFile::IsPinned() {
|
||||||
|
CacheFileAutoLock lock(this);
|
||||||
|
return mPinned;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
CacheFile::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
|
CacheFile::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
|
||||||
{
|
{
|
||||||
|
|
@ -469,6 +479,7 @@ CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
|
||||||
bool mAlreadyDoomed;
|
bool mAlreadyDoomed;
|
||||||
} autoDoom(aHandle);
|
} autoDoom(aHandle);
|
||||||
|
|
||||||
|
RefPtr<CacheFileMetadata> metadata;
|
||||||
nsCOMPtr<CacheFileListener> listener;
|
nsCOMPtr<CacheFileListener> listener;
|
||||||
bool isNew = false;
|
bool isNew = false;
|
||||||
nsresult retval = NS_OK;
|
nsresult retval = NS_OK;
|
||||||
|
|
@ -571,20 +582,22 @@ CacheFile::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (listener) {
|
||||||
|
lock.Unlock();
|
||||||
|
listener->OnFileReady(retval, isNew);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (listener) {
|
MOZ_ASSERT(NS_SUCCEEDED(aResult));
|
||||||
listener->OnFileReady(retval, isNew);
|
MOZ_ASSERT(!mMetadata);
|
||||||
return NS_OK;
|
MOZ_ASSERT(mListener);
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(aResult));
|
metadata = mMetadata = new CacheFileMetadata(mHandle, mKey);
|
||||||
MOZ_ASSERT(!mMetadata);
|
}
|
||||||
MOZ_ASSERT(mListener);
|
|
||||||
|
|
||||||
mMetadata = new CacheFileMetadata(mHandle, mKey);
|
mMetadata = new CacheFileMetadata(mHandle, mKey);
|
||||||
|
|
||||||
rv = mMetadata->ReadMetadata(this);
|
rv = metadata->ReadMetadata(this);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
mListener.swap(listener);
|
mListener.swap(listener);
|
||||||
listener->OnFileReady(rv, false);
|
listener->OnFileReady(rv, false);
|
||||||
|
|
@ -611,40 +624,42 @@ CacheFile::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
|
||||||
nsresult
|
nsresult
|
||||||
CacheFile::OnMetadataRead(nsresult aResult)
|
CacheFile::OnMetadataRead(nsresult aResult)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mListener);
|
nsCOMPtr<CacheFileListener> listener;
|
||||||
|
|
||||||
LOG(("CacheFile::OnMetadataRead() [this=%p, rv=0x%08x]", this, aResult));
|
|
||||||
|
|
||||||
bool isNew = false;
|
bool isNew = false;
|
||||||
if (NS_SUCCEEDED(aResult)) {
|
{
|
||||||
mPinned = mMetadata->Pinned();
|
CacheFileAutoLock lock(this);
|
||||||
mReady = true;
|
MOZ_ASSERT(mListener);
|
||||||
mDataSize = mMetadata->Offset();
|
|
||||||
if (mDataSize == 0 && mMetadata->ElementsSize() == 0) {
|
|
||||||
isNew = true;
|
|
||||||
mMetadata->MarkDirty();
|
|
||||||
} else {
|
|
||||||
const char *altData = mMetadata->GetElement(CacheFileUtils::kAltDataKey);
|
|
||||||
if (altData &&
|
|
||||||
(NS_FAILED(CacheFileUtils::ParseAlternativeDataInfo(
|
|
||||||
altData, &mAltDataOffset, nullptr)) ||
|
|
||||||
(mAltDataOffset > mDataSize))) {
|
|
||||||
// alt-metadata cannot be parsed or alt-data offset is invalid
|
|
||||||
mMetadata->InitEmptyMetadata();
|
|
||||||
isNew = true;
|
|
||||||
mAltDataOffset = -1;
|
|
||||||
mDataSize = 0;
|
|
||||||
} else {
|
|
||||||
CacheFileAutoLock lock(this);
|
|
||||||
PreloadChunks(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InitIndexEntry();
|
LOG(("CacheFile::OnMetadataRead() [this=%p, rv=0x%08]", this,
|
||||||
|
static_cast<uint32_t>(aResult)));
|
||||||
|
|
||||||
|
if (NS_SUCCEEDED(aResult)) {
|
||||||
|
mPinned = mMetadata->Pinned();
|
||||||
|
mReady = true;
|
||||||
|
mDataSize = mMetadata->Offset();
|
||||||
|
if (mDataSize == 0 && mMetadata->ElementsSize() == 0) {
|
||||||
|
mMetadata->MarkDirty();
|
||||||
|
} else {
|
||||||
|
const char* altData =
|
||||||
|
mMetadata->GetElement(CacheFileUtils::kAltDataKey);
|
||||||
|
if (altData && (NS_FAILED(CacheFileUtils::ParseAlternativeDataInfo(
|
||||||
|
altData, &mAltDataOffset, nullptr)) ||
|
||||||
|
(mAltDataOffset > mDataSize))) {
|
||||||
|
// alt-metadata cannot be parsed or alt-data offset is invalid
|
||||||
|
mMetadata->InitEmptyMetadata();
|
||||||
|
isNew = true;
|
||||||
|
mAltDataOffset = -1;
|
||||||
|
mDataSize = 0;
|
||||||
|
} else {
|
||||||
|
PreloadChunks(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InitIndexEntry();
|
||||||
|
}
|
||||||
|
mListener.swap(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<CacheFileListener> listener;
|
|
||||||
mListener.swap(listener);
|
|
||||||
listener->OnFileReady(aResult, isNew);
|
listener->OnFileReady(aResult, isNew);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -1021,6 +1036,7 @@ CacheFile::Doom(CacheFileListener *aCallback)
|
||||||
nsresult
|
nsresult
|
||||||
CacheFile::DoomLocked(CacheFileListener *aCallback)
|
CacheFile::DoomLocked(CacheFileListener *aCallback)
|
||||||
{
|
{
|
||||||
|
AssertOwnsLock();
|
||||||
MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
|
MOZ_ASSERT(mHandle || mMemoryOnly || mOpeningFile);
|
||||||
|
|
||||||
LOG(("CacheFile::DoomLocked() [this=%p, listener=%p]", this, aCallback));
|
LOG(("CacheFile::DoomLocked() [this=%p, listener=%p]", this, aCallback));
|
||||||
|
|
@ -1232,6 +1248,7 @@ CacheFile::GetFetchCount(uint32_t *_retval)
|
||||||
nsresult
|
nsresult
|
||||||
CacheFile::GetDiskStorageSizeInKB(uint32_t *aDiskStorageSize)
|
CacheFile::GetDiskStorageSizeInKB(uint32_t *aDiskStorageSize)
|
||||||
{
|
{
|
||||||
|
CacheFileAutoLock lock(this);
|
||||||
if (!mHandle) {
|
if (!mHandle) {
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
@ -1255,30 +1272,6 @@ CacheFile::OnFetched()
|
||||||
return mMetadata->OnFetched();
|
return mMetadata->OnFetched();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
CacheFile::Lock()
|
|
||||||
{
|
|
||||||
mLock.Lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CacheFile::Unlock()
|
|
||||||
{
|
|
||||||
// move the elements out of mObjsToRelease
|
|
||||||
// so that they can be released after we unlock
|
|
||||||
nsTArray<RefPtr<nsISupports>> objs;
|
|
||||||
objs.SwapElements(mObjsToRelease);
|
|
||||||
|
|
||||||
mLock.Unlock();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CacheFile::AssertOwnsLock() const
|
|
||||||
{
|
|
||||||
mLock.AssertCurrentThreadOwns();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CacheFile::ReleaseOutsideLock(RefPtr<nsISupports> aObject)
|
CacheFile::ReleaseOutsideLock(RefPtr<nsISupports> aObject)
|
||||||
{
|
{
|
||||||
|
|
@ -2010,6 +2003,7 @@ CacheFile::NotifyChunkListeners(uint32_t aIndex, nsresult aResult,
|
||||||
bool
|
bool
|
||||||
CacheFile::HaveChunkListeners(uint32_t aIndex)
|
CacheFile::HaveChunkListeners(uint32_t aIndex)
|
||||||
{
|
{
|
||||||
|
AssertOwnsLock();
|
||||||
ChunkListeners *listeners;
|
ChunkListeners *listeners;
|
||||||
mChunkListeners.Get(aIndex, &listeners);
|
mChunkListeners.Get(aIndex, &listeners);
|
||||||
return !!listeners;
|
return !!listeners;
|
||||||
|
|
@ -2270,6 +2264,7 @@ CacheFile::SetError(nsresult aStatus)
|
||||||
nsresult
|
nsresult
|
||||||
CacheFile::InitIndexEntry()
|
CacheFile::InitIndexEntry()
|
||||||
{
|
{
|
||||||
|
AssertOwnsLock();
|
||||||
MOZ_ASSERT(mHandle);
|
MOZ_ASSERT(mHandle);
|
||||||
|
|
||||||
if (mHandle->IsDoomed())
|
if (mHandle->IsDoomed())
|
||||||
|
|
|
||||||
|
|
@ -112,9 +112,10 @@ public:
|
||||||
nsresult OnFetched();
|
nsresult OnFetched();
|
||||||
|
|
||||||
bool DataSize(int64_t* aSize);
|
bool DataSize(int64_t* aSize);
|
||||||
void Key(nsACString& aKey) { aKey = mKey; }
|
void Key(nsACString& aKey);
|
||||||
bool IsDoomed();
|
bool IsDoomed();
|
||||||
bool IsPinned() const { return mPinned; }
|
bool IsPinned();
|
||||||
|
// Returns true when there is a potentially unfinished write operation.
|
||||||
bool IsWriteInProgress();
|
bool IsWriteInProgress();
|
||||||
|
|
||||||
// Memory reporting
|
// Memory reporting
|
||||||
|
|
@ -131,10 +132,16 @@ private:
|
||||||
|
|
||||||
virtual ~CacheFile();
|
virtual ~CacheFile();
|
||||||
|
|
||||||
void Lock();
|
void Lock() { mLock.Lock(); }
|
||||||
void Unlock();
|
void Unlock() {
|
||||||
void AssertOwnsLock() const;
|
// move the elements out of mObjsToRelease
|
||||||
void ReleaseOutsideLock(RefPtr<nsISupports> aObject);
|
// so that they can be released after we unlock
|
||||||
|
nsTArray<RefPtr<nsISupports>> objs = std::move(mObjsToRelease);
|
||||||
|
|
||||||
|
mLock.Unlock();
|
||||||
|
}
|
||||||
|
void AssertOwnsLock() const { mLock.AssertCurrentThreadOwns(); }
|
||||||
|
void ReleaseOutsideLock(RefPtr<nsISupports> aObject);
|
||||||
|
|
||||||
enum ECallerType {
|
enum ECallerType {
|
||||||
READER = 0,
|
READER = 0,
|
||||||
|
|
|
||||||
|
|
@ -472,6 +472,7 @@ void
|
||||||
CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
|
CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
|
||||||
{
|
{
|
||||||
AssertOwnsLock();
|
AssertOwnsLock();
|
||||||
|
mFile->AssertOwnsLock(); // For thread-safety analysis
|
||||||
|
|
||||||
LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
|
LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
|
||||||
this, aCallback));
|
this, aCallback));
|
||||||
|
|
@ -586,6 +587,7 @@ void
|
||||||
CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen)
|
CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen)
|
||||||
{
|
{
|
||||||
AssertOwnsLock();
|
AssertOwnsLock();
|
||||||
|
mFile->AssertOwnsLock(); // For thread-safety analysis
|
||||||
|
|
||||||
// UpdateDataSize() is called only when we've written some data to the chunk
|
// UpdateDataSize() is called only when we've written some data to the chunk
|
||||||
// and we never write data anymore once some error occurs.
|
// and we never write data anymore once some error occurs.
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,7 @@ NS_IMETHODIMP
|
||||||
CacheFileInputStream::Seek(int32_t whence, int64_t offset)
|
CacheFileInputStream::Seek(int32_t whence, int64_t offset)
|
||||||
{
|
{
|
||||||
CacheFileAutoLock lock(mFile);
|
CacheFileAutoLock lock(mFile);
|
||||||
|
mFile->AssertOwnsLock(); // For thread-safety analysis
|
||||||
|
|
||||||
LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%lld]",
|
LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%lld]",
|
||||||
this, whence, offset));
|
this, whence, offset));
|
||||||
|
|
@ -395,6 +396,7 @@ NS_IMETHODIMP
|
||||||
CacheFileInputStream::Tell(int64_t *_retval)
|
CacheFileInputStream::Tell(int64_t *_retval)
|
||||||
{
|
{
|
||||||
CacheFileAutoLock lock(mFile);
|
CacheFileAutoLock lock(mFile);
|
||||||
|
mFile->AssertOwnsLock(); // For thread-safety analysis
|
||||||
|
|
||||||
if (mClosed) {
|
if (mClosed) {
|
||||||
LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
|
LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ CacheFileOutputStream::Write(const char * aBuf, uint32_t aCount,
|
||||||
uint32_t *_retval)
|
uint32_t *_retval)
|
||||||
{
|
{
|
||||||
CacheFileAutoLock lock(mFile);
|
CacheFileAutoLock lock(mFile);
|
||||||
|
mFile->AssertOwnsLock(); // For thread-safety analysis
|
||||||
|
|
||||||
LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
|
LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
|
||||||
|
|
||||||
|
|
@ -257,6 +258,7 @@ NS_IMETHODIMP
|
||||||
CacheFileOutputStream::Seek(int32_t whence, int64_t offset)
|
CacheFileOutputStream::Seek(int32_t whence, int64_t offset)
|
||||||
{
|
{
|
||||||
CacheFileAutoLock lock(mFile);
|
CacheFileAutoLock lock(mFile);
|
||||||
|
mFile->AssertOwnsLock(); // For thread-safety analysis
|
||||||
|
|
||||||
LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]",
|
LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]",
|
||||||
this, whence, offset));
|
this, whence, offset));
|
||||||
|
|
@ -298,6 +300,7 @@ NS_IMETHODIMP
|
||||||
CacheFileOutputStream::Tell(int64_t *_retval)
|
CacheFileOutputStream::Tell(int64_t *_retval)
|
||||||
{
|
{
|
||||||
CacheFileAutoLock lock(mFile);
|
CacheFileAutoLock lock(mFile);
|
||||||
|
mFile->AssertOwnsLock(); // For thread-safety analysis
|
||||||
|
|
||||||
if (mClosed) {
|
if (mClosed) {
|
||||||
LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
|
LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
|
||||||
|
|
@ -371,6 +374,8 @@ void CacheFileOutputStream::NotifyCloseListener()
|
||||||
void
|
void
|
||||||
CacheFileOutputStream::ReleaseChunk()
|
CacheFileOutputStream::ReleaseChunk()
|
||||||
{
|
{
|
||||||
|
mFile->AssertOwnsLock();
|
||||||
|
|
||||||
LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]",
|
LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]",
|
||||||
this, mChunk->Index()));
|
this, mChunk->Index()));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,8 @@ private:
|
||||||
|
|
||||||
mozilla::Monitor mMonitor;
|
mozilla::Monitor mMonitor;
|
||||||
PRThread* mThread;
|
PRThread* mThread;
|
||||||
|
// Only set in Init(), before the thread is started, which reads it but never
|
||||||
|
// writes
|
||||||
UniquePtr<detail::BlockingIOWatcher> mBlockingIOWatcher;
|
UniquePtr<detail::BlockingIOWatcher> mBlockingIOWatcher;
|
||||||
Atomic<nsIThread *> mXPCOMThread;
|
Atomic<nsIThread *> mXPCOMThread;
|
||||||
Atomic<uint32_t, Relaxed> mLowestLevelWaiting;
|
Atomic<uint32_t, Relaxed> mLowestLevelWaiting;
|
||||||
|
|
@ -128,7 +130,7 @@ private:
|
||||||
// Raised when nsIEventTarget.Dispatch() is called on this thread
|
// Raised when nsIEventTarget.Dispatch() is called on this thread
|
||||||
Atomic<bool, Relaxed> mHasXPCOMEvents;
|
Atomic<bool, Relaxed> mHasXPCOMEvents;
|
||||||
// See YieldAndRerun() above
|
// See YieldAndRerun() above
|
||||||
bool mRerunCurrentEvent;
|
bool mRerunCurrentEvent; // Only accessed on the cache thread
|
||||||
// Signal to process all pending events and then shutdown
|
// Signal to process all pending events and then shutdown
|
||||||
// Synchronized by mMonitor
|
// Synchronized by mMonitor
|
||||||
bool mShutdown;
|
bool mShutdown;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue