// Main thread via OnProcessNextEvent below bool nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait, uint32_t recursionDepth) { // The next native event to be processed may trigger our NativeEventCallback, // in which case we do not want it to process any thread events since we'll // do that when this function returns. // // If the next native event is not our NativeEventCallback, then we may end // up recursing into this function. // // However, if the next native event is not our NativeEventCallback, but it // results in another native event loop, then our NativeEventCallback could // fire and it will see mEventloopNestingState as eEventloopOther. // EventloopNestingState prevVal = mEventloopNestingState; mEventloopNestingState = eEventloopXPCOM; ++mEventloopNestingLevel; bool result = ProcessNextNativeEvent(mayWait); // Make sure that any sync sections registered during this most recent event // are run now. This is not considered a stable state because we're not back // to the event loop yet. RunSyncSections(false, recursionDepth); --mEventloopNestingLevel; mEventloopNestingState = prevVal; return result; }
// Called from the main thread NS_IMETHODIMP nsBaseAppShell::AfterProcessNextEvent(nsIThreadInternal *thr, uint32_t recursionDepth) { // We've just finished running an event, so we're in a stable state. RunSyncSections(true, recursionDepth); return NS_OK; }
NS_IMETHODIMP nsBaseAppShell::RunInStableState(nsIRunnable* aRunnable) { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); // Record the synchronous section, and run it with any others once // we reach a stable state. mSyncSections.AppendObject(aRunnable); // Ensure we've got a pending event, else the callbacks will never run. nsIThread* thread = NS_GetCurrentThread(); if (!NS_HasPendingEvents(thread) && NS_FAILED(thread->Dispatch(new nsRunnable(), NS_DISPATCH_NORMAL))) { // Failed to dispatch dummy event to cause sync sections to run, thread // is probably done processing events, just run the sync sections now. RunSyncSections(); } return NS_OK; }
void nsBaseAppShell::ScheduleSyncSection(already_AddRefed<nsIRunnable> aRunnable, bool aStable) { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); nsIThread* thread = NS_GetCurrentThread(); // Add this runnable to our list of synchronous sections. SyncSection* section = mSyncSections.AppendElement(); section->mStable = aStable; section->mRunnable = aRunnable; // If aStable is false then this synchronous section is supposed to run before // the next event at the current nesting level. Record the event loop nesting // level and the thread recursion level so that the synchronous section will // run at the proper time. if (!aStable) { section->mEventloopNestingLevel = mEventloopNestingLevel; nsCOMPtr<nsIThreadInternal> threadInternal = do_QueryInterface(thread); NS_ASSERTION(threadInternal, "This should never fail!"); uint32_t recursionLevel; if (NS_FAILED(threadInternal->GetRecursionDepth(&recursionLevel))) { NS_ERROR("This should never fail!"); } // Due to the weird way that the thread recursion counter is implemented we // subtract one from the recursion level if we have one. section->mThreadRecursionLevel = recursionLevel ? recursionLevel - 1 : 0; } // Ensure we've got a pending event, else the callbacks will never run. if (!NS_HasPendingEvents(thread) && !DispatchDummyEvent(thread)) { RunSyncSections(true, 0); } }
// Called from the main thread NS_IMETHODIMP nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, uint32_t recursionDepth) { if (mBlockNativeEvent) { if (!mayWait) return NS_OK; // Hmm, we're in a nested native event loop and would like to get // back to it ASAP, but it seems a gecko event has caused us to // spin up a nested XPCOM event loop (eg. modal window), so we // really must start processing native events here again. mBlockNativeEvent = false; if (NS_HasPendingEvents(thr)) OnDispatchedEvent(thr); // in case we blocked it earlier } PRIntervalTime start = PR_IntervalNow(); PRIntervalTime limit = THREAD_EVENT_STARVATION_LIMIT; // Unblock outer nested wait loop (below). if (mBlockedWait) *mBlockedWait = false; bool *oldBlockedWait = mBlockedWait; mBlockedWait = &mayWait; // When mayWait is true, we need to make sure that there is an event in the // thread's event queue before we return. Otherwise, the thread will block // on its event queue waiting for an event. bool needEvent = mayWait; // Reset prior to invoking DoProcessNextNativeEvent which might cause // NativeEventCallback to process gecko events. mProcessedGeckoEvents = false; if (mFavorPerf <= 0 && start > mSwitchTime + mStarvationDelay) { // Favor pending native events PRIntervalTime now = start; bool keepGoing; do { mLastNativeEventTime = now; keepGoing = DoProcessNextNativeEvent(false, recursionDepth); } while (keepGoing && ((now = PR_IntervalNow()) - start) < limit); } else { // Avoid starving native events completely when in performance mode if (start - mLastNativeEventTime > limit) { mLastNativeEventTime = start; DoProcessNextNativeEvent(false, recursionDepth); } } while (!NS_HasPendingEvents(thr) && !mProcessedGeckoEvents) { // If we have been asked to exit from Run, then we should not wait for // events to process. Note that an inner nested event loop causes // 'mayWait' to become false too, through 'mBlockedWait'. if (mExiting) mayWait = false; mLastNativeEventTime = PR_IntervalNow(); if (!DoProcessNextNativeEvent(mayWait, recursionDepth) || !mayWait) break; } mBlockedWait = oldBlockedWait; // Make sure that the thread event queue does not block on its monitor, as // it normally would do if it did not have any pending events. To avoid // that, we simply insert a dummy event into its queue during shutdown. if (needEvent && !mExiting && !NS_HasPendingEvents(thr)) { DispatchDummyEvent(thr); } // We're about to run an event, so we're in a stable state. RunSyncSections(true, recursionDepth); return NS_OK; }