void CacheEntry::DoomAlreadyRemoved() { LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this)); mIsDoomed = true; if (!CacheStorageService::IsOnManagementThread()) { mozilla::MutexAutoLock lock(mLock); BackgroundOp(Ops::DOOM); return; } CacheStorageService::Self()->UnregisterEntry(this); { mozilla::MutexAutoLock lock(mLock); if (mCallbacks.Length() || mReadOnlyCallbacks.Length()) { // Must force post here since may be indirectly called from // InvokeCallbacks of this entry and we don't want reentrancy here. BackgroundOp(Ops::CALLBACKS, true); } } if (NS_SUCCEEDED(mFileStatus)) { nsresult rv = mFile->Doom(mDoomCallback ? this : nullptr); if (NS_SUCCEEDED(rv)) { LOG((" file doomed")); return; } } OnFileDoomed(NS_OK); }
CacheEntry::Handle* CacheEntry::NewWriteHandle() { mozilla::MutexAutoLock lock(mLock); BackgroundOp(Ops::FRECENCYUPDATE); return (mWriter = new Handle(this)); }
NS_IMETHODIMP CacheEntry::Run() { MOZ_ASSERT(CacheStorageService::IsOnManagementThread()); mozilla::MutexAutoLock lock(mLock); BackgroundOp(mBackgroundOperations.Grab()); return NS_OK; }
CacheEntryHandle* CacheEntry::NewWriteHandle() { mozilla::MutexAutoLock lock(mLock); // Ignore the OPEN_SECRETLY flag on purpose here, which should actually be // used only along with OPEN_READONLY, but there is no need to enforce that. BackgroundOp(Ops::FRECENCYUPDATE); return (mWriter = NewHandle()); }
void CacheEntry::DoomAlreadyRemoved() { LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this)); mozilla::MutexAutoLock lock(mLock); mIsDoomed = 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). DoomFile(); // Must force post here since may be indirectly called from // InvokeCallbacks of this entry and we don't want reentrancy here. BackgroundOp(Ops::CALLBACKS, true); // Process immediately when on the management thread. BackgroundOp(Ops::UNREGISTER); }
void CacheEntry::TransferCallbacks(CacheEntry const& aFromEntry) { mozilla::MutexAutoLock lock(mLock); LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]", this, &aFromEntry)); mCallbacks.AppendObjects(aFromEntry.mCallbacks); mReadOnlyCallbacks.AppendObjects(aFromEntry.mReadOnlyCallbacks); if (aFromEntry.mHasMainThreadOnlyCallback) mHasMainThreadOnlyCallback = true; if (mCallbacks.Length() || mReadOnlyCallbacks.Length()) BackgroundOp(Ops::CALLBACKS, true); }
NS_IMETHODIMP CacheEntry::Recreate(nsICacheEntry **_retval) { LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState))); mozilla::MutexAutoLock lock(mLock); nsRefPtr<CacheEntryHandle> handle = ReopenTruncated(nullptr); if (handle) { handle.forget(_retval); return NS_OK; } BackgroundOp(Ops::CALLBACKS, true); return NS_OK; }
NS_IMETHODIMP CacheEntry::AsyncDoom(nsICacheEntryDoomCallback *aCallback) { LOG(("CacheEntry::AsyncDoom [this=%p]", this)); { mozilla::MutexAutoLock lock(mLock); if (mIsDoomed || mDoomCallback) return NS_ERROR_IN_PROGRESS; // to aggregate have DOOMING state mIsDoomed = true; mDoomCallback = aCallback; BackgroundOp(Ops::DOOM); } // Immediately remove the entry from the storage hash table CacheStorageService::Self()->RemoveEntry(this); return NS_OK; }
void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry) { mozilla::MutexAutoLock lock(mLock); LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]", this, &aFromEntry)); if (!mCallbacks.Length()) mCallbacks.SwapElements(aFromEntry.mCallbacks); else mCallbacks.AppendElements(aFromEntry.mCallbacks); uint32_t callbacksLength = mCallbacks.Length(); if (callbacksLength) { // Carry the entry reference (unfortunatelly, needs to be done manually...) for (uint32_t i = 0; i < callbacksLength; ++i) mCallbacks[i].ExchangeEntry(this); BackgroundOp(Ops::CALLBACKS, true); } }
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; uint32_t const state = mState; // When we are here, the entry must be loaded from disk MOZ_ASSERT(state > LOADING || mIsDoomed); bool onAvailThread; rv = aCallback.OnAvailThread(&onAvailThread); if (NS_FAILED(rv)) { LOG((" target thread dead?")); return; } if (!onAvailThread) { // Dispatch to the right thread nsRefPtr<AvailableCallbackRunnable> event = new AvailableCallbackRunnable(this, aCallback); rv = aCallback.mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); LOG((" redispatched, (rv = 0x%08x)", rv)); return; } if (NS_SUCCEEDED(mFileStatus) && !aCallback.mSecret) { // Let the last-fetched and fetch-count properties be updated. mFile->OnFetched(); } if (mIsDoomed || aCallback.mNotWanted) { LOG((" doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND")); aCallback.mCallback->OnCacheEntryAvailable( nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND); return; } if (state == READY) { LOG((" ready/has-meta, notifying OCEA with entry and NS_OK")); if (!aCallback.mSecret) { mozilla::MutexAutoLock lock(mLock); BackgroundOp(Ops::FRECENCYUPDATE); } nsRefPtr<CacheEntryHandle> handle = NewHandle(); aCallback.mCallback->OnCacheEntryAvailable( handle, false, nullptr, NS_OK); return; } // R/O callbacks may do revalidation, let them fall through if (aCallback.mReadOnly && !aCallback.mRevalidating) { LOG((" r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND")); aCallback.mCallback->OnCacheEntryAvailable( nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND); return; } // This is a new or potentially non-valid entry and needs to be fetched first. // The CacheEntryHandle blocks other consumers until the channel // either releases the entry or marks metadata as filled or whole entry valid, // i.e. until MetaDataReady() or SetValid() on the entry is called respectively. // Consumer will be responsible to fill or validate the entry metadata and data. nsRefPtr<CacheEntryHandle> handle = NewWriteHandle(); rv = aCallback.mCallback->OnCacheEntryAvailable( handle, state == WRITING, nullptr, NS_OK); if (NS_FAILED(rv)) { LOG((" writing/revalidating failed (0x%08x)", rv)); // Consumer given a new entry failed to take care of the entry. OnHandleClosed(handle); return; } LOG((" writing/revalidating")); }
bool CacheEntry::Load(bool aTruncate, bool aPriority) { LOG(("CacheEntry::Load [this=%p, trunc=%d]", this, aTruncate)); mLock.AssertCurrentThreadOwns(); if (mState > LOADING) { LOG((" already loaded")); return false; } if (mState == LOADING) { LOG((" already loading")); return true; } mState = LOADING; MOZ_ASSERT(!mFile); nsresult rv; nsAutoCString fileKey; rv = HashingKeyWithStorage(fileKey); // Check the index under two conditions for two states and take appropriate action: // 1. When this is a disk entry and not told to truncate, check there is a disk file. // If not, set the 'truncate' flag to true so that this entry will open instantly // as a new one. // 2. When this is a memory-only entry, check there is a disk file. // If there is or could be, doom that file. if ((!aTruncate || !mUseDisk) && NS_SUCCEEDED(rv)) { // Check the index right now to know we have or have not the entry // as soon as possible. CacheIndex::EntryStatus status; 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")); 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)); CacheFileIOManager::DoomFileByKey(fileKey, nullptr); } break; } } } mFile = new CacheFile(); BackgroundOp(Ops::REGISTER); bool directLoad = aTruncate || !mUseDisk; if (directLoad) { // mLoadStart will be used to calculate telemetry of life-time of this entry. // Low resulution is then enough. mLoadStart = TimeStamp::NowLoRes(); } else { mLoadStart = TimeStamp::Now(); } { mozilla::MutexAutoUnlock unlock(mLock); LOG((" performing load, file=%p", mFile.get())); if (NS_SUCCEEDED(rv)) { rv = mFile->Init(fileKey, aTruncate, !mUseDisk, aPriority, directLoad ? nullptr : this); } if (NS_FAILED(rv)) { mFileStatus = rv; AsyncDoom(nullptr); return false; } } if (directLoad) { // Just fake the load has already been done as "new". mFileStatus = NS_OK; mState = EMPTY; } return mState == LOADING; }
void CacheEntry::InvokeAvailableCallback(nsICacheEntryOpenCallback* aCallback, bool aReadOnly, bool aNotWanted) { LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]", this, StateString(mState), aCallback, aReadOnly, aNotWanted)); uint32_t const state = mState; // When we are here, the entry must be loaded from disk MOZ_ASSERT(state > LOADING || mIsDoomed); if (!NS_IsMainThread()) { // Must happen on the main thread :( nsRefPtr<AvailableCallbackRunnable> event = new AvailableCallbackRunnable(this, aCallback, aReadOnly, aNotWanted); NS_DispatchToMainThread(event); return; } // This happens only on the main thread / :( / if (mIsDoomed || aNotWanted) { LOG((" doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND")); aCallback->OnCacheEntryAvailable(nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND); return; } if (state == READY) { LOG((" ready/has-meta, notifying OCEA with entry and NS_OK")); { mozilla::MutexAutoLock lock(mLock); BackgroundOp(Ops::FRECENCYUPDATE); } aCallback->OnCacheEntryAvailable(this, false, nullptr, NS_OK); return; } if (aReadOnly) { LOG((" r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND")); aCallback->OnCacheEntryAvailable(nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND); return; } // This is a new or potentially non-valid entry and needs to be fetched first. // The Handle blocks other consumers until the channel // either releases the entry or marks metadata as filled or whole entry valid, // i.e. until MetaDataReady() or SetValid() on the entry is called respectively. // Consumer will be responsible to fill or validate the entry metadata and data. nsRefPtr<Handle> handle = NewWriteHandle(); nsresult rv = aCallback->OnCacheEntryAvailable(handle, state == WRITING, nullptr, NS_OK); if (NS_FAILED(rv)) { LOG((" writing/revalidating failed (0x%08x)", rv)); // Consumer given a new entry failed to take care of the entry. OnWriterClosed(handle); return; } LOG((" writing/revalidating")); }