NS_IMETHODIMP CacheEntry::SetValid() { LOG(("CacheEntry::SetValid [this=%p, state=%s]", this, StateString(mState))); nsCOMPtr<nsIOutputStream> outputStream; { mozilla::MutexAutoLock lock(mLock); MOZ_ASSERT(mState > EMPTY); mState = READY; mHasData = true; InvokeCallbacks(); outputStream.swap(mOutputStream); } if (outputStream) { LOG((" abandoning phantom output stream")); outputStream->Close(); } return NS_OK; }
void CacheEntry::RememberCallback(Callback & aCallback) { mLock.AssertCurrentThreadOwns(); LOG(("CacheEntry::RememberCallback [this=%p, cb=%p, state=%s]", this, aCallback.mCallback.get(), StateString(mState))); mCallbacks.AppendElement(aCallback); }
void CacheEntry::OnHandleClosed(CacheEntryHandle const* aHandle) { LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState), aHandle)); nsCOMPtr<nsIOutputStream> outputStream; { mozilla::MutexAutoLock lock(mLock); if (mWriter != aHandle) { LOG((" not the writer")); return; } if (mOutputStream) { // No one took our internal output stream, so there are no data // and output stream has to be open symultaneously with input stream // on this entry again. mHasData = false; } outputStream.swap(mOutputStream); mWriter = nullptr; if (mState == WRITING) { LOG((" reverting to state EMPTY - write failed")); mState = EMPTY; } else if (mState == REVALIDATING) { LOG((" reverting to state READY - reval failed")); mState = READY; } if (mState == READY && !mHasData) { // We may get to this state when following steps happen: // 1. a new entry is given to a consumer // 2. the consumer calls MetaDataReady(), we transit to READY // 3. abandons the entry w/o opening the output stream, mHasData left false // // In this case any following consumer will get a ready entry (with metadata) // but in state like the entry data write was still happening (was in progress) // and will indefinitely wait for the entry data or even the entry itself when // RECHECK_AFTER_WRITE is returned from onCacheEntryCheck. LOG((" we are in READY state, pretend we have data regardless it" " has actully been never touched")); mHasData = true; } InvokeCallbacks(); } if (outputStream) { LOG((" abandoning phantom output stream")); outputStream->Close(); } }
bool CacheEntry::Purge(uint32_t aWhat) { LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat)); MOZ_ASSERT(CacheStorageService::IsOnManagementThread()); switch (aWhat) { case PURGE_DATA_ONLY_DISK_BACKED: case PURGE_WHOLE_ONLY_DISK_BACKED: // This is an in-memory only entry, don't purge it if (!mUseDisk) { LOG((" not using disk")); return false; } } if (mState == WRITING || mState == LOADING || mFrecency == 0) { // In-progress (write or load) entries should (at least for consistency and from // the logical point of view) stay in memory. // Zero-frecency entries are those which have never been given to any consumer, those // are actually very fresh and should not go just because frecency had not been set // so far. LOG((" state=%s, frecency=%1.10f", StateString(mState), mFrecency)); return false; } switch (aWhat) { case PURGE_WHOLE_ONLY_DISK_BACKED: case PURGE_WHOLE: { if (!CacheStorageService::Self()->RemoveEntry(this, true)) { LOG((" not purging, still referenced")); return false; } CacheStorageService::Self()->UnregisterEntry(this); // Entry removed it self from control arrays, return true return true; } case PURGE_DATA_ONLY_DISK_BACKED: { NS_ENSURE_SUCCESS(mFileStatus, false); mFile->ThrowMemoryCachedData(); // Entry has been left in control arrays, return false (not purged) return false; } } LOG((" ?")); return false; }
void R28(std::vector<Dynamic>& __ret) { //arguments Dynamic hllr__4 = __ret.back(); __ret.pop_back(); Dynamic hllr__3 = Dynamic (__ret.back()); __ret.pop_back(); Dynamic hllr__2 = __ret.back(); __ret.pop_back(); Dynamic hllr__1 = __ret.back(); __ret.pop_back(); Dynamic hllr__0 = __ret.back(); __ret.pop_back(); Dynamic retval; retval = StateString(ptr<State>(new StateArgScope((States&)hllr__3))); __ret.push_back(retval); }
//============================== // OvrScrollBarComponent::SetScrollState void OvrScrollBarComponent::SetScrollState( VRMenuObject * self, const eScrollBarState state ) { eScrollBarState lastState = CurrentScrollState; CurrentScrollState = state; if ( CurrentScrollState == lastState ) { return; } switch ( CurrentScrollState ) { case SCROLL_STATE_NONE: break; case SCROLL_STATE_FADE_IN: if ( lastState == SCROLL_STATE_HIDDEN || lastState == SCROLL_STATE_FADE_OUT ) { LOG( "%s to %s", StateString( lastState ), StateString( CurrentScrollState ) ); Fader.StartFadeIn(); } break; case SCROLL_STATE_VISIBLE: self->SetVisible( true ); break; case SCROLL_STATE_FADE_OUT: if ( lastState == SCROLL_STATE_VISIBLE || lastState == SCROLL_STATE_FADE_IN ) { LOG( "%s to %s", StateString( lastState ), StateString( CurrentScrollState ) ); Fader.StartFadeOut(); } break; case SCROLL_STATE_HIDDEN: self->SetVisible( false ); break; default: OVR_ASSERT( false ); break; } }
NS_IMETHODIMP CacheEntry::MetaDataReady() { mozilla::MutexAutoLock lock(mLock); LOG(("CacheEntry::MetaDataReady [this=%p, state=%s]", this, StateString(mState))); MOZ_ASSERT(mState > EMPTY); if (mState == WRITING) mState = READY; InvokeCallbacks(); return NS_OK; }
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; }
void CacheEntry::OnWriterClosed(Handle const* aHandle) { LOG(("CacheEntry::OnWriterClosed [this=%p, state=%s, handle=%p]", this, StateString(mState), aHandle)); nsCOMPtr<nsIOutputStream> outputStream; { mozilla::MutexAutoLock lock(mLock); if (mWriter != aHandle) { LOG((" not the current writer")); return; } if (mOutputStream) { // No one took our internal output stream, so there are no data // and output stream has to be open symultaneously with input stream // on this entry again. mHasData = false; } outputStream.swap(mOutputStream); mWriter = nullptr; if (mState == WRITING) { LOG((" reverting to state EMPTY - write failed")); mState = EMPTY; } else if (mState == REVALIDATING) { LOG((" reverting to state READY - reval failed")); mState = READY; } InvokeCallbacks(); } if (outputStream) { LOG((" abandoning phantom output stream")); outputStream->Close(); } }
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 bypassIfBusy = aFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY; bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE; bool priority = aFlags & nsICacheStorage::OPEN_PRIORITY; bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED; bool secret = aFlags & nsICacheStorage::OPEN_SECRETLY; MOZ_ASSERT(!readonly || !truncate, "Bad flags combination"); MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry"); Callback callback(this, aCallback, readonly, multithread, secret); if (!Open(callback, truncate, priority, bypassIfBusy)) { // We get here when the callback wants to bypass cache when it's busy. LOG((" writing or revalidating, callback wants to bypass cache")); callback.mNotWanted = true; InvokeAvailableCallback(callback); } }
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::InvokeCallback(Callback & aCallback) { LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]", this, StateString(mState), aCallback.mCallback.get())); mLock.AssertCurrentThreadOwns(); // When this entry is doomed we want to notify the callback any time if (!mIsDoomed) { // When we are here, the entry must be loaded from disk MOZ_ASSERT(mState > LOADING); if (mState == WRITING || mState == REVALIDATING) { // Prevent invoking other callbacks since one of them is now writing // or revalidating this entry. No consumers should get this entry // until metadata are filled with values downloaded from the server // or the entry revalidated and output stream has been opened. LOG((" entry is being written/revalidated, callback bypassed")); return false; } // mRecheckAfterWrite flag already set means the callback has already passed // the onCacheEntryCheck call. Until the current write is not finished this // callback will be bypassed. if (!aCallback.mRecheckAfterWrite) { if (!aCallback.mReadOnly) { if (mState == EMPTY) { // Advance to writing state, we expect to invoke the callback and let // it fill content of this entry. Must set and check the state here // to prevent more then one mState = WRITING; LOG((" advancing to WRITING state")); } if (!aCallback.mCallback) { // We can be given no callback only in case of recreate, it is ok // to advance to WRITING state since the caller of recreate is expected // to write this entry now. return true; } } if (mState == READY) { // Metadata present, validate the entry uint32_t checkResult; { // mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck mozilla::MutexAutoUnlock unlock(mLock); nsresult rv = aCallback.mCallback->OnCacheEntryCheck( this, nullptr, &checkResult); LOG((" OnCacheEntryCheck: rv=0x%08x, result=%d", rv, checkResult)); if (NS_FAILED(rv)) checkResult = ENTRY_NOT_WANTED; } aCallback.mRevalidating = checkResult == ENTRY_NEEDS_REVALIDATION; switch (checkResult) { case ENTRY_WANTED: // Nothing more to do here, the consumer is responsible to handle // the result of OnCacheEntryCheck it self. // Proceed to callback... break; case RECHECK_AFTER_WRITE_FINISHED: LOG((" consumer will check on the entry again after write is done")); // The consumer wants the entry to complete first. aCallback.mRecheckAfterWrite = true; break; case ENTRY_NEEDS_REVALIDATION: LOG((" will be holding callbacks until entry is revalidated")); // State is READY now and from that state entry cannot transit to any other // state then REVALIDATING for which cocurrency is not an issue. Potentially // no need to lock here. mState = REVALIDATING; break; case ENTRY_NOT_WANTED: LOG((" consumer not interested in the entry")); // Do not give this entry to the consumer, it is not interested in us. aCallback.mNotWanted = true; break; } } } } if (aCallback.mCallback) { if (!mIsDoomed && aCallback.mRecheckAfterWrite) { // If we don't have data and the callback wants a complete entry, // don't invoke now. bool bypass = !mHasData; if (!bypass && NS_SUCCEEDED(mFileStatus)) { int64_t _unused; bypass = !mFile->DataSize(&_unused); } if (bypass) { LOG((" bypassing, entry data still being written")); return false; } // Entry is complete now, do the check+avail call again aCallback.mRecheckAfterWrite = false; return InvokeCallback(aCallback); } mozilla::MutexAutoUnlock unlock(mLock); InvokeAvailableCallback(aCallback); } return true; }
bool CacheEntry::Purge(uint32_t aWhat) { LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat)); MOZ_ASSERT(CacheStorageService::IsOnManagementThread()); switch (aWhat) { case PURGE_DATA_ONLY_DISK_BACKED: case PURGE_WHOLE_ONLY_DISK_BACKED: // This is an in-memory only entry, don't purge it if (!mUseDisk) { LOG((" not using disk")); return false; } } if (mState == WRITING || mState == LOADING || mFrecency == 0) { // In-progress (write or load) entries should (at least for consistency and from // the logical point of view) stay in memory. // Zero-frecency entries are those which have never been given to any consumer, those // are actually very fresh and should not go just because frecency had not been set // so far. LOG((" state=%s, frecency=%1.10f", StateString(mState), mFrecency)); return false; } if (NS_SUCCEEDED(mFileStatus) && mFile->IsWriteInProgress()) { // The file is used when there are open streams or chunks/metadata still waiting for // write. In this case, this entry cannot be purged, otherwise reopenned entry // would may not even find the data on disk - CacheFile is not shared and cannot be // left orphan when its job is not done, hence keep the whole entry. LOG((" file still under use")); return false; } switch (aWhat) { case PURGE_WHOLE_ONLY_DISK_BACKED: case PURGE_WHOLE: { if (!CacheStorageService::Self()->RemoveEntry(this, true)) { LOG((" not purging, still referenced")); return false; } CacheStorageService::Self()->UnregisterEntry(this); // Entry removed it self from control arrays, return true return true; } case PURGE_DATA_ONLY_DISK_BACKED: { NS_ENSURE_SUCCESS(mFileStatus, false); mFile->ThrowMemoryCachedData(); // Entry has been left in control arrays, return false (not purged) return false; } } LOG((" ?")); return false; }
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")); }
bool CacheEntry::InvokeCallback(nsICacheEntryOpenCallback* aCallback, bool aReadOnly) { LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]", this, StateString(mState), aCallback)); mLock.AssertCurrentThreadOwns(); bool notWanted = false; if (!mIsDoomed) { // When we are here, the entry must be loaded from disk MOZ_ASSERT(mState > LOADING); if (mState == WRITING || mState == REVALIDATING) { // Prevent invoking other callbacks since one of them is now writing // or revalidating this entry. No consumers should get this entry // until metadata are filled with values downloaded from the server // or the entry revalidated and output stream has been opened. LOG((" entry is being written/revalidated, callback bypassed")); return false; } if (!aReadOnly) { if (mState == EMPTY) { // Advance to writing state, we expect to invoke the callback and let // it fill content of this entry. Must set and check the state here // to prevent more then one mState = WRITING; LOG((" advancing to WRITING state")); } if (!aCallback) { // We can be given no callback only in case of recreate, it is ok // to advance to WRITING state since the caller of recreate is expected // to write this entry now. return true; } if (mState == READY) { // Metadata present, validate the entry uint32_t checkResult; { // mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck mozilla::MutexAutoUnlock unlock(mLock); nsresult rv = aCallback->OnCacheEntryCheck(this, nullptr, &checkResult); LOG((" OnCacheEntryCheck: rv=0x%08x, result=%d", rv, checkResult)); if (NS_FAILED(rv)) checkResult = ENTRY_WANTED; } switch (checkResult) { case ENTRY_WANTED: // Nothing more to do here, the consumer is responsible to handle // the result of OnCacheEntryCheck it self. // Proceed to callback... break; case ENTRY_NEEDS_REVALIDATION: LOG((" will be holding callbacks until entry is revalidated")); // State is READY now and from that state entry cannot transit to any other // state then REVALIDATING for which cocurrency is not an issue. Potentially // no need to lock here. mState = REVALIDATING; break; case ENTRY_NOT_WANTED: LOG((" consumer not interested in the entry")); // Do not give this entry to the consumer, it is not interested in us. notWanted = true; break; } } } } if (aCallback) { mozilla::MutexAutoUnlock unlock(mLock); InvokeAvailableCallback(aCallback, aReadOnly, notWanted); } return true; }