NS_IMETHODIMP STSThreadPoolListener::OnThreadCreated() { if (IsNuwaProcess()) { NuwaMarkCurrentThread(nullptr, nullptr); } return NS_OK; }
void ProcessLink::SendMessage(Message *msg) { mChan->AssertWorkerThread(); mChan->mMonitor->AssertCurrentThreadOwns(); #ifdef MOZ_NUWA_PROCESS // Parent to child: check whether we are sending some unexpected message to // the Nuwa process. if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) { switch (msg->type()) { case mozilla::dom::PNuwa::Msg_Fork__ID: case mozilla::dom::PNuwa::Reply_AddNewProcess__ID: case mozilla::dom::PContent::Msg_NotifyPhoneStateChange__ID: case mozilla::dom::PContent::Msg_ActivateA11y__ID: case mozilla::hal_sandbox::PHal::Msg_NotifyNetworkChange__ID: case GOODBYE_MESSAGE_TYPE: break; default: #ifdef DEBUG MOZ_CRASH(); #else // In optimized build, message will be dropped. printf_stderr("Sending message to frozen Nuwa"); return; #endif } } #if defined(DEBUG) // Nuwa to parent: check whether we are currently blocked. if (IsNuwaProcess() && mIsBlocked) { char* jsstack = PrintJSStack(); printf_stderr("Fatal error: sending a message to the chrome process" "with a blocked IPC channel from \n%s", jsstack ? jsstack : "<no JS stack>"); JS_smprintf_free(jsstack); MOZ_CRASH(); } #endif #endif mIOLoop->PostTask( FROM_HERE, NewRunnableMethod(mTransport, &Transport::Send, msg)); }
static void ConnectImageBridgeInChildProcess(Transport* aTransport, ProcessId aOtherPid) { // Bind the IPC channel to the image bridge thread. sImageBridgeChildSingleton->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ChildSide); #ifdef MOZ_NUWA_PROCESS if (IsNuwaProcess()) { sImageBridgeChildThread ->message_loop()->PostTask(FROM_HERE, NewRunnableFunction(NuwaMarkCurrentThread, (void (*)(void *))nullptr, (void *)nullptr)); } #endif }
nsresult nsStreamTransportService::Init() { mPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID); NS_ENSURE_STATE(mPool); // Configure the pool mPool->SetName(NS_LITERAL_CSTRING("StreamTrans")); mPool->SetThreadLimit(25); mPool->SetIdleThreadLimit(1); mPool->SetIdleThreadTimeout(PR_SecondsToInterval(30)); #ifdef MOZ_NUWA_PROCESS if (IsNuwaProcess()) { mPool->SetListener(new STSThreadPoolListener()); } #endif nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); if (obsSvc) obsSvc->AddObserver(this, "xpcom-shutdown-threads", false); return NS_OK; }
bool NuwaChild::RecvFork() { #ifdef MOZ_NUWA_PROCESS if (!IsNuwaProcess()) { NS_ERROR( nsPrintfCString( "Terminating child process %d for unauthorized IPC message: " "RecvFork(%d)", getpid()).get()); return false; } nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(&NuwaFork); MOZ_ASSERT(runnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); return true; #else NS_ERROR("NuwaChild::RecvFork() not implemented!"); return false; #endif }
void ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Side aSide) { NS_PRECONDITION(aTransport, "need transport layer"); // FIXME need to check for valid channel mTransport = aTransport; // FIXME figure out whether we're in parent or child, grab IO loop // appropriately bool needOpen = true; if(aIOLoop) { // We're a child or using the new arguments. Either way, we // need an open. needOpen = true; mChan->mSide = (aSide == UnknownSide) ? ChildSide : aSide; } else { NS_PRECONDITION(aSide == UnknownSide, "expected default side arg"); // parent mChan->mSide = ParentSide; needOpen = false; aIOLoop = XRE_GetIOMessageLoop(); } mIOLoop = aIOLoop; NS_ASSERTION(mIOLoop, "need an IO loop"); NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop"); { MonitorAutoLock lock(*mChan->mMonitor); if (needOpen) { // Transport::Connect() has not been called. Call it so // we start polling our pipe and processing outgoing // messages. mIOLoop->PostTask( FROM_HERE, NewRunnableMethod(this, &ProcessLink::OnChannelOpened)); } else { // Transport::Connect() has already been called. Take // over the channel from the previous listener and process // any queued messages. mIOLoop->PostTask( FROM_HERE, NewRunnableMethod(this, &ProcessLink::OnTakeConnectedChannel)); } #ifdef MOZ_NUWA_PROCESS if (IsNuwaProcess() && Preferences::GetBool("dom.ipc.processPrelaunch.testMode")) { // The pref value is turned on in a deadlock test against the Nuwa // process. The sleep here makes it easy to trigger the deadlock // that an IPC channel is still opening but the worker loop is // already frozen. sleep(5); } #endif // Should not wait here if something goes wrong with the channel. while (!mChan->Connected() && mChan->mChannelState != ChannelError) { mChan->mMonitor->Wait(); } } }
NS_IMETHODIMP nsSocketTransportService::Run() { #ifdef MOZ_NUWA_PROCESS if (IsNuwaProcess()) { NuwaMarkCurrentThread(nullptr, nullptr); } #endif SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount)); psm::InitializeSSLServerCertVerificationThreads(); gSocketThread = PR_GetCurrentThread(); { DebugMutexAutoLock lock(mLock); mPollableEvent.reset(new PollableEvent()); // // NOTE: per bug 190000, this failure could be caused by Zone-Alarm // or similar software. // // NOTE: per bug 191739, this failure could also be caused by lack // of a loopback device on Windows and OS/2 platforms (it creates // a loopback socket pair on these platforms to implement a pollable // event object). if we can't create a pollable event, then we'll // have to "busy wait" to implement the socket event queue :-( // if (!mPollableEvent->Valid()) { mPollableEvent = nullptr; NS_WARNING("running socket transport thread without a pollable event"); SOCKET_LOG(("running socket transport thread without a pollable event")); } mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; mPollList[0].out_flags = 0; } mRawThread = NS_GetCurrentThread(); // hook ourselves up to observe event processing for this thread nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(mRawThread); threadInt->SetObserver(this); // make sure the pseudo random number generator is seeded on this thread srand(static_cast<unsigned>(PR_Now())); // For the calculation of the duration of the last cycle (i.e. the last for-loop // iteration before shutdown). TimeStamp startOfCycleForLastCycleCalc; int numberOfPendingEventsLastCycle; // For measuring of the poll iteration duration without time spent blocked // in poll(). TimeStamp pollCycleStart; // Time blocked in poll(). TimeDuration singlePollDuration; // For calculating the time needed for a new element to run. TimeStamp startOfIteration; TimeStamp startOfNextIteration; int numberOfPendingEvents; // If there is too many pending events queued, we will run some poll() // between them and the following variable is cumulative time spent // blocking in poll(). TimeDuration pollDuration; for (;;) { bool pendingEvents = false; numberOfPendingEvents = 0; numberOfPendingEventsLastCycle = 0; if (mTelemetryEnabledPref) { startOfCycleForLastCycleCalc = TimeStamp::NowLoRes(); startOfNextIteration = TimeStamp::NowLoRes(); } pollDuration = 0; do { if (mTelemetryEnabledPref) { pollCycleStart = TimeStamp::NowLoRes(); } DoPollIteration(&singlePollDuration); if (mTelemetryEnabledPref && !pollCycleStart.IsNull()) { Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME, singlePollDuration.ToMilliseconds()); Telemetry::AccumulateTimeDelta( Telemetry::STS_POLL_CYCLE, pollCycleStart + singlePollDuration, TimeStamp::NowLoRes()); pollDuration += singlePollDuration; } mRawThread->HasPendingEvents(&pendingEvents); if (pendingEvents) { if (!mServingPendingQueue) { nsresult rv = Dispatch(NewRunnableMethod(this, &nsSocketTransportService::MarkTheLastElementOfPendingQueue), nsIEventTarget::DISPATCH_NORMAL); if (NS_FAILED(rv)) { NS_WARNING("Could not dispatch a new event on the " "socket thread."); } else { mServingPendingQueue = true; } if (mTelemetryEnabledPref) { startOfIteration = startOfNextIteration; // Everything that comes after this point will // be served in the next iteration. If no even // arrives, startOfNextIteration will be reset at the // beginning of each for-loop. startOfNextIteration = TimeStamp::NowLoRes(); } } TimeStamp eventQueueStart = TimeStamp::NowLoRes(); do { NS_ProcessNextEvent(mRawThread); numberOfPendingEvents++; pendingEvents = false; mRawThread->HasPendingEvents(&pendingEvents); } while (pendingEvents && mServingPendingQueue && ((TimeStamp::NowLoRes() - eventQueueStart).ToMilliseconds() < mMaxTimePerPollIter)); if (mTelemetryEnabledPref && !mServingPendingQueue && !startOfIteration.IsNull()) { Telemetry::AccumulateTimeDelta( Telemetry::STS_POLL_AND_EVENTS_CYCLE, startOfIteration + pollDuration, TimeStamp::NowLoRes()); Telemetry::Accumulate( Telemetry::STS_NUMBER_OF_PENDING_EVENTS, numberOfPendingEvents); numberOfPendingEventsLastCycle += numberOfPendingEvents; numberOfPendingEvents = 0; pollDuration = 0; } } } while (pendingEvents); bool goingOffline = false; // now that our event queue is empty, check to see if we should exit { DebugMutexAutoLock lock(mLock); if (mShuttingDown) { if (mTelemetryEnabledPref && !startOfCycleForLastCycleCalc.IsNull()) { Telemetry::Accumulate( Telemetry::STS_NUMBER_OF_PENDING_EVENTS_IN_THE_LAST_CYCLE, numberOfPendingEventsLastCycle); Telemetry::AccumulateTimeDelta( Telemetry::STS_POLL_AND_EVENT_THE_LAST_CYCLE, startOfCycleForLastCycleCalc, TimeStamp::NowLoRes()); } break; } if (mGoingOffline) { mGoingOffline = false; goingOffline = true; } } // Avoid potential deadlock if (goingOffline) Reset(true); } SOCKET_LOG(("STS shutting down thread\n")); // detach all sockets, including locals Reset(false); // Final pass over the event queue. This makes sure that events posted by // socket detach handlers get processed. NS_ProcessPendingEvents(mRawThread); gSocketThread = nullptr; psm::StopSSLServerCertVerificationThreads(); SOCKET_LOG(("STS thread exit\n")); return NS_OK; }
void ClosingService::ThreadFunc() { PR_SetCurrentThreadName("Closing Service"); #ifdef MOZ_NUWA_PROCESS if (IsNuwaProcess()) { NuwaMarkCurrentThread(nullptr, nullptr); } #endif for (;;) { PRFileDesc *fd; { mozilla::MonitorAutoLock mon(mMonitor); while (!mShutdown && (mQueue.Length() == 0)) { mon.Wait(); } if (mShutdown) { // If we are in shutdown leak the rest of the sockets. for (uint32_t i = 0; i < mQueue.Length(); i++) { fd = mQueue[i]; // If the ClosingService layer is the first layer above // PR_NSPR_IO_LAYER we are not going to leak anything, but PR_Close // will not be called. PR_Free(fd); } mQueue.Clear(); return; } fd = mQueue[0]; mQueue.RemoveElementAt(0); } // Leave lock before closing socket. It can block for a long time and in // case we accidentally attach this layer twice this would cause deadlock. bool tcp = (PR_GetDescType(PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER)) == PR_DESC_SOCKET_TCP); PRIntervalTime closeStarted = PR_IntervalNow(); fd->methods->close(fd); // Post telemetry. if (tcp) { SendPRCloseTelemetry(closeStarted, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE); } else { SendPRCloseTelemetry(closeStarted, Telemetry::PRCLOSE_UDP_BLOCKING_TIME_NORMAL, Telemetry::PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN, Telemetry::PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE, Telemetry::PRCLOSE_UDP_BLOCKING_TIME_LINK_CHANGE, Telemetry::PRCLOSE_UDP_BLOCKING_TIME_OFFLINE); } } }
void ThreadMain(void*) { PR_SetCurrentThreadName("Hang Monitor"); #ifdef MOZ_NUWA_PROCESS if (IsNuwaProcess()) { NuwaMarkCurrentThread(nullptr, nullptr); } #endif MonitorAutoLock lock(*gMonitor); // In order to avoid issues with the hang monitor incorrectly triggering // during a general system stop such as sleeping, the monitor thread must // run twice to trigger hang protection. PRIntervalTime lastTimestamp = 0; int waitCount = 0; #ifdef REPORT_CHROME_HANGS Telemetry::ProcessedStack stack; int32_t systemUptime = -1; int32_t firefoxUptime = -1; UniquePtr<HangAnnotations> annotations; #endif while (true) { if (gShutdown) { return; // Exit the thread } // avoid rereading the volatile value in this loop PRIntervalTime timestamp = gTimestamp; PRIntervalTime now = PR_IntervalNow(); if (timestamp != PR_INTERVAL_NO_WAIT && now < timestamp) { // 32-bit overflow, reset for another waiting period timestamp = 1; // lowest legal PRInterval value } if (timestamp != PR_INTERVAL_NO_WAIT && timestamp == lastTimestamp && gTimeout > 0) { ++waitCount; #ifdef REPORT_CHROME_HANGS // Capture the chrome-hang stack + Firefox & system uptimes after // the minimum hang duration has been reached (not when the hang ends) if (waitCount == 2) { GetChromeHangReport(stack, systemUptime, firefoxUptime); annotations = ChromeHangAnnotatorCallout(); } #else // This is the crash-on-hang feature. // See bug 867313 for the quirk in the waitCount comparison if (waitCount >= 2) { int32_t delay = int32_t(PR_IntervalToSeconds(now - timestamp)); if (delay >= gTimeout) { MonitorAutoUnlock unlock(*gMonitor); Crash(); } } #endif } else { #ifdef REPORT_CHROME_HANGS if (waitCount >= 2) { uint32_t hangDuration = PR_IntervalToSeconds(now - lastTimestamp); Telemetry::RecordChromeHang(hangDuration, stack, systemUptime, firefoxUptime, Move(annotations)); stack.Clear(); } #endif lastTimestamp = timestamp; waitCount = 0; } PRIntervalTime timeout; if (gTimeout <= 0) { timeout = PR_INTERVAL_NO_TIMEOUT; } else { timeout = PR_MillisecondsToInterval(gTimeout * 500); } lock.Wait(timeout); } }