Bug 1791179 - Fix mistaken conditional in StaticPrefs. r=dragana, a=RyanVM
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* 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"MessagePump.h"#include"nsIThread.h"#include"nsITimer.h"#include"nsICancelableRunnable.h"#include"base/basictypes.h"#include"base/logging.h"#include"base/scoped_nsautorelease_pool.h"#include"mozilla/Assertions.h"#include"mozilla/DebugOnly.h"#include"nsComponentManagerUtils.h"#include"nsDebug.h"#include"nsServiceManagerUtils.h"#include"nsString.h"#include"nsThreadUtils.h"#include"nsTimerImpl.h"#include"nsXULAppAPI.h"#include"prthread.h"usingbase::TimeTicks;usingnamespacemozilla::ipc;#ifdef DEBUGstaticMessagePump::Delegate*gFirstDelegate;#endifnamespacemozilla{namespaceipc{classDoWorkRunnablefinal:publicCancelableRunnable,publicnsITimerCallback{public:explicitDoWorkRunnable(MessagePump*aPump):CancelableRunnable("ipc::DoWorkRunnable"),mPump(aPump){MOZ_ASSERT(aPump);}NS_DECL_ISUPPORTS_INHERITEDNS_DECL_NSIRUNNABLENS_DECL_NSITIMERCALLBACKnsresultCancel()override;private:~DoWorkRunnable()=default;MessagePump*mPump;// DoWorkRunnable is designed as a stateless singleton. Do not add stateful// members here!};}/* namespace ipc */}/* namespace mozilla */MessagePump::MessagePump(nsISerialEventTarget*aEventTarget):mEventTarget(aEventTarget){mDoWorkEvent=newDoWorkRunnable(this);}MessagePump::~MessagePump()=default;voidMessagePump::Run(MessagePump::Delegate*aDelegate){MOZ_ASSERT(keep_running_);MOZ_RELEASE_ASSERT(NS_IsMainThread(),"Use mozilla::ipc::MessagePumpForNonMainThreads instead!");MOZ_RELEASE_ASSERT(!mEventTarget);nsIThread*thisThread=NS_GetCurrentThread();MOZ_ASSERT(thisThread);mDelayedWorkTimer=NS_NewTimer();MOZ_ASSERT(mDelayedWorkTimer);base::ScopedNSAutoreleasePoolautoReleasePool;for(;;){autoReleasePool.Recycle();booldid_work=NS_ProcessNextEvent(thisThread,false)?true:false;if(!keep_running_)break;// NB: it is crucial *not* to directly call |aDelegate->DoWork()|// here. To ensure that MessageLoop tasks and XPCOM events have// equal priority, we sensitively rely on processing exactly one// Task per DoWorkRunnable XPCOM event.did_work|=aDelegate->DoDelayedWork(&delayed_work_time_);if(did_work&&delayed_work_time_.is_null())mDelayedWorkTimer->Cancel();if(!keep_running_)break;if(did_work)continue;did_work=aDelegate->DoIdleWork();if(!keep_running_)break;if(did_work)continue;// This will either sleep or process an event.NS_ProcessNextEvent(thisThread,true);}mDelayedWorkTimer->Cancel();keep_running_=true;}voidMessagePump::ScheduleWork(){// Make sure the event loop wakes up.if(mEventTarget){mEventTarget->Dispatch(mDoWorkEvent,NS_DISPATCH_NORMAL);}else{// Some things (like xpcshell) don't use the app shell and so Run hasn't// been called. We still need to wake up the main thread.NS_DispatchToMainThread(mDoWorkEvent);}event_.Signal();}voidMessagePump::ScheduleWorkForNestedLoop(){// This method is called when our MessageLoop has just allowed// nested tasks. In our setup, whenever that happens we know that// DoWork() will be called "soon", so there's no need to pay the// cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent).}voidMessagePump::ScheduleDelayedWork(constbase::TimeTicks&aDelayedTime){// To avoid racing on mDelayedWorkTimer, we need to be on the same thread as// ::Run().MOZ_RELEASE_ASSERT((!mEventTarget&&NS_IsMainThread())||mEventTarget->IsOnCurrentThread());if(!mDelayedWorkTimer){mDelayedWorkTimer=NS_NewTimer();if(!mDelayedWorkTimer){// Called before XPCOM has started up? We can't do this correctly.NS_WARNING("Delayed task might not run!");delayed_work_time_=aDelayedTime;return;}}if(!delayed_work_time_.is_null()){mDelayedWorkTimer->Cancel();}delayed_work_time_=aDelayedTime;// TimeDelta's constructor initializes to 0base::TimeDeltadelay;if(aDelayedTime>base::TimeTicks::Now())delay=aDelayedTime-base::TimeTicks::Now();uint32_tdelayMS=uint32_t(delay.InMilliseconds());mDelayedWorkTimer->InitWithCallback(mDoWorkEvent,delayMS,nsITimer::TYPE_ONE_SHOT);}nsISerialEventTarget*MessagePump::GetXPCOMThread(){if(mEventTarget){returnmEventTarget;}// Main threadreturnGetMainThreadSerialEventTarget();}voidMessagePump::DoDelayedWork(base::MessagePump::Delegate*aDelegate){aDelegate->DoDelayedWork(&delayed_work_time_);if(!delayed_work_time_.is_null()){ScheduleDelayedWork(delayed_work_time_);}}NS_IMPL_ISUPPORTS_INHERITED(DoWorkRunnable,CancelableRunnable,nsITimerCallback)NS_IMETHODIMPDoWorkRunnable::Run(){MessageLoop*loop=MessageLoop::current();MOZ_ASSERT(loop);boolnestableTasksAllowed=loop->NestableTasksAllowed();// MessageLoop::RunTask() disallows nesting, but our Frankenventloop will// always dispatch DoWork() below from what looks to MessageLoop like a nested// context. So we unconditionally allow nesting here.loop->SetNestableTasksAllowed(true);loop->DoWork();loop->SetNestableTasksAllowed(nestableTasksAllowed);returnNS_OK;}NS_IMETHODIMPDoWorkRunnable::Notify(nsITimer*aTimer){MessageLoop*loop=MessageLoop::current();MOZ_ASSERT(loop);boolnestableTasksAllowed=loop->NestableTasksAllowed();loop->SetNestableTasksAllowed(true);mPump->DoDelayedWork(loop);loop->SetNestableTasksAllowed(nestableTasksAllowed);returnNS_OK;}nsresultDoWorkRunnable::Cancel(){// Workers require cancelable runnables, but we can't really cancel cleanly// here. If we don't process this runnable then we will leave something// unprocessed in the message_loop. Therefore, eagerly complete our work// instead by immediately calling Run(). Run() should be called separately// after this. Unfortunately we cannot use flags to verify this because// DoWorkRunnable is a stateless singleton that can be in the event queue// multiple times simultaneously.MOZ_ALWAYS_SUCCEEDS(Run());returnNS_OK;}voidMessagePumpForChildProcess::Run(base::MessagePump::Delegate*aDelegate){if(mFirstRun){MOZ_ASSERT(aDelegate&&!gFirstDelegate);#ifdef DEBUGgFirstDelegate=aDelegate;#endifmFirstRun=false;if(NS_FAILED(XRE_RunAppShell())){NS_WARNING("Failed to run app shell?!");}MOZ_ASSERT(aDelegate&&aDelegate==gFirstDelegate);#ifdef DEBUGgFirstDelegate=nullptr;#endifreturn;}MOZ_ASSERT(aDelegate&&aDelegate==gFirstDelegate);// We can get to this point in startup with Tasks in our loop's// incoming_queue_ or pending_queue_, but without a matching// DoWorkRunnable(). In MessagePump::Run() above, we sensitively// depend on *not* directly calling delegate->DoWork(), because that// prioritizes Tasks above XPCOM events. However, from this point// forward, any Task posted to our loop is guaranteed to have a// DoWorkRunnable enqueued for it.//// So we just flush the pending work here and move on.MessageLoop*loop=MessageLoop::current();boolnestableTasksAllowed=loop->NestableTasksAllowed();loop->SetNestableTasksAllowed(true);while(aDelegate->DoWork());loop->SetNestableTasksAllowed(nestableTasksAllowed);// Really run.mozilla::ipc::MessagePump::Run(aDelegate);}voidMessagePumpForNonMainThreads::Run(base::MessagePump::Delegate*aDelegate){MOZ_ASSERT(keep_running_);MOZ_RELEASE_ASSERT(!NS_IsMainThread(),"Use mozilla::ipc::MessagePump instead!");nsIThread*thread=NS_GetCurrentThread();MOZ_RELEASE_ASSERT(mEventTarget->IsOnCurrentThread());mDelayedWorkTimer=NS_NewTimer(mEventTarget);MOZ_ASSERT(mDelayedWorkTimer);// Chromium event notifications to be processed will be received by this// event loop as a DoWorkRunnables via ScheduleWork. Chromium events that// were received before our thread is valid, however, will not generate// runnable wrappers. We must process any of these before we enter this// loop, or we will forever have unprocessed chromium messages in our queue.//// Note we would like to request a flush of the chromium event queue// using a runnable on the xpcom side, but some thread implementations// (dom workers) get cranky if we call ScheduleWork here (ScheduleWork// calls dispatch on mEventTarget) before the thread processes an event. As// such, clear the queue manually.while(aDelegate->DoWork()){}base::ScopedNSAutoreleasePoolautoReleasePool;for(;;){autoReleasePool.Recycle();booldidWork=NS_ProcessNextEvent(thread,false)?true:false;if(!keep_running_){break;}didWork|=aDelegate->DoDelayedWork(&delayed_work_time_);if(didWork&&delayed_work_time_.is_null()){mDelayedWorkTimer->Cancel();}if(!keep_running_){break;}if(didWork){continue;}DebugOnly<bool>didIdleWork=aDelegate->DoIdleWork();MOZ_ASSERT(!didIdleWork);if(!keep_running_){break;}if(didWork){continue;}// This will either sleep or process an event.NS_ProcessNextEvent(thread,true);}mDelayedWorkTimer->Cancel();keep_running_=true;}#if defined(XP_WIN)NS_IMPL_QUERY_INTERFACE(MessagePumpForNonMainUIThreads,nsIThreadObserver)# define CHECK_QUIT_STATE \ { \ if (state_->should_quit) { \ break; \ } \ }voidMessagePumpForNonMainUIThreads::DoRunLoop(){MOZ_RELEASE_ASSERT(!NS_IsMainThread(),"Use mozilla::ipc::MessagePump instead!");// If this is a chromium thread and no nsThread is associated// with it, this call will create a new nsThread.nsIThread*thread=NS_GetCurrentThread();MOZ_ASSERT(thread);// Set the main thread observer so we can wake up when// xpcom events need to get processed.nsCOMPtr<nsIThreadInternal>ti(do_QueryInterface(thread));MOZ_ASSERT(ti);ti->SetObserver(this);base::ScopedNSAutoreleasePoolautoReleasePool;for(;;){autoReleasePool.Recycle();booldidWork=NS_ProcessNextEvent(thread,false);didWork|=ProcessNextWindowsMessage();CHECK_QUIT_STATEdidWork|=state_->delegate->DoWork();CHECK_QUIT_STATEdidWork|=state_->delegate->DoDelayedWork(&delayed_work_time_);if(didWork&&delayed_work_time_.is_null()){KillTimer(message_hwnd_,reinterpret_cast<UINT_PTR>(this));}CHECK_QUIT_STATEif(didWork){continue;}DebugOnly<bool>didIdleWork=state_->delegate->DoIdleWork();MOZ_ASSERT(!didIdleWork);CHECK_QUIT_STATESetInWait();boolhasWork=NS_HasPendingEvents(thread);if(didWork||hasWork){ClearInWait();continue;}WaitForWork();// Calls MsgWaitForMultipleObjectsEx(QS_ALLINPUT)ClearInWait();}ClearInWait();ti->SetObserver(nullptr);}NS_IMETHODIMPMessagePumpForNonMainUIThreads::OnDispatchedEvent(){// If our thread is sleeping in DoRunLoop's call to WaitForWork() and an// event posts to the nsIThread event queue - break our thread out of// chromium's WaitForWork.if(GetInWait()){ScheduleWork();}returnNS_OK;}NS_IMETHODIMPMessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal*thread,boolmayWait){returnNS_OK;}NS_IMETHODIMPMessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal*thread,booleventWasProcessed){returnNS_OK;}#endif // XP_WIN#if defined(MOZ_WIDGET_ANDROID)voidMessagePumpForAndroidUI::Run(Delegate*delegate){MOZ_CRASH("MessagePumpForAndroidUI should never be Run.");}voidMessagePumpForAndroidUI::Quit(){MOZ_CRASH("MessagePumpForAndroidUI should never be Quit.");}voidMessagePumpForAndroidUI::ScheduleWork(){MOZ_CRASH("MessagePumpForAndroidUI should never ScheduleWork");}voidMessagePumpForAndroidUI::ScheduleDelayedWork(constTimeTicks&delayed_work_time){MOZ_CRASH("MessagePumpForAndroidUI should never ScheduleDelayedWork");}#endif // defined(MOZ_WIDGET_ANDROID)