nsresult nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration) { SOCKET_LOG(("STS poll iter\n")); int32_t i, count; // // poll loop // // walk active list backwards to see if any sockets should actually be // idle, then walk the idle list backwards to see if any idle sockets // should become active. take care to check only idle sockets that // were idle to begin with ;-) // count = mIdleCount; for (i=mActiveCount-1; i>=0; --i) { //--- SOCKET_LOG((" active [%u] { handler=%p condition=%x pollflags=%hu }\n", i, mActiveList[i].mHandler, mActiveList[i].mHandler->mCondition, mActiveList[i].mHandler->mPollFlags)); //--- if (NS_FAILED(mActiveList[i].mHandler->mCondition)) DetachSocket(mActiveList, &mActiveList[i]); else { uint16_t in_flags = mActiveList[i].mHandler->mPollFlags; if (in_flags == 0) MoveToIdleList(&mActiveList[i]); else { // update poll flags mPollList[i+1].in_flags = in_flags; mPollList[i+1].out_flags = 0; } } } for (i=count-1; i>=0; --i) { //--- SOCKET_LOG((" idle [%u] { handler=%p condition=%x pollflags=%hu }\n", i, mIdleList[i].mHandler, mIdleList[i].mHandler->mCondition, mIdleList[i].mHandler->mPollFlags)); //--- if (NS_FAILED(mIdleList[i].mHandler->mCondition)) DetachSocket(mIdleList, &mIdleList[i]); else if (mIdleList[i].mHandler->mPollFlags != 0) MoveToPollList(&mIdleList[i]); } SOCKET_LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount)); #if defined(XP_WIN) // 30 active connections is the historic limit before firefox 7's 256. A few // windows systems have troubles with the higher limit, so actively probe a // limit the first time we exceed 30. if ((mActiveCount > 30) && !mProbedMaxCount) ProbeMaxCount(); #endif // Measures seconds spent while blocked on PR_Poll uint32_t pollInterval = 0; int32_t n = 0; *pollDuration = 0; if (!gIOService->IsNetTearingDown()) { // Let's not do polling during shutdown. n = Poll(&pollInterval, pollDuration); } if (n < 0) { SOCKET_LOG((" PR_Poll error [%d] os error [%d]\n", PR_GetError(), PR_GetOSError())); } else { // // service "active" sockets... // uint32_t numberOfOnSocketReadyCalls = 0; for (i=0; i<int32_t(mActiveCount); ++i) { PRPollDesc &desc = mPollList[i+1]; SocketContext &s = mActiveList[i]; if (n > 0 && desc.out_flags != 0) { s.mElapsedTime = 0; s.mHandler->OnSocketReady(desc.fd, desc.out_flags); numberOfOnSocketReadyCalls++; } // check for timeout errors unless disabled... else if (s.mHandler->mPollTimeout != UINT16_MAX) { // update elapsed time counter // (NOTE: We explicitly cast UINT16_MAX to be an unsigned value // here -- otherwise, some compilers will treat it as signed, // which makes them fire signed/unsigned-comparison build // warnings for the comparison against 'pollInterval'.) if (MOZ_UNLIKELY(pollInterval > static_cast<uint32_t>(UINT16_MAX) - s.mElapsedTime)) s.mElapsedTime = UINT16_MAX; else s.mElapsedTime += uint16_t(pollInterval); // check for timeout expiration if (s.mElapsedTime >= s.mHandler->mPollTimeout) { s.mElapsedTime = 0; s.mHandler->OnSocketReady(desc.fd, -1); numberOfOnSocketReadyCalls++; } } } if (mTelemetryEnabledPref) { Telemetry::Accumulate( Telemetry::STS_NUMBER_OF_ONSOCKETREADY_CALLS, numberOfOnSocketReadyCalls); } // // check for "dead" sockets and remove them (need to do this in // reverse order obviously). // for (i=mActiveCount-1; i>=0; --i) { if (NS_FAILED(mActiveList[i].mHandler->mCondition)) DetachSocket(mActiveList, &mActiveList[i]); } if (n != 0 && (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT))) { MutexAutoLock lock(mLock); // acknowledge pollable event (should not block) if (mPollableEvent && ((mPollList[0].out_flags & PR_POLL_EXCEPT) || !mPollableEvent->Clear())) { // On Windows, the TCP loopback connection in the // pollable event may become broken when a laptop // switches between wired and wireless networks or // wakes up from hibernation. We try to create a // new pollable event. If that fails, we fall back // on "busy wait". NS_WARNING("Trying to repair mPollableEvent"); mPollableEvent.reset(new PollableEvent()); if (!mPollableEvent->Valid()) { mPollableEvent = nullptr; } SOCKET_LOG(("running socket transport thread without " "a pollable event now valid=%d", !!mPollableEvent)); mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; mPollList[0].out_flags = 0; } } } return NS_OK; }
NS_IMETHODIMP nsSocketTransportService::Run() { LOG(("nsSocketTransportService::Run")); gSocketThread = PR_GetCurrentThread(); // // add thread event to poll list (mThreadEvent may be NULL) // mPollList[0].fd = mThreadEvent; mPollList[0].in_flags = PR_POLL_READ; mPollList[0].out_flags = 0; PRInt32 i, count; // // poll loop // PRBool active = PR_TRUE; while (active) { // // walk active list backwards to see if any sockets should actually be // idle, then walk the idle list backwards to see if any idle sockets // should become active. take care to check only idle sockets that // were idle to begin with ;-) // count = mIdleCount; for (i=mActiveCount-1; i>=0; --i) { //--- LOG((" active [%u] { handler=%x condition=%x pollflags=%hu }\n", i, mActiveList[i].mHandler, mActiveList[i].mHandler->mCondition, mActiveList[i].mHandler->mPollFlags)); //--- if (NS_FAILED(mActiveList[i].mHandler->mCondition)) DetachSocket(&mActiveList[i]); else { PRUint16 in_flags = mActiveList[i].mHandler->mPollFlags; if (in_flags == 0) MoveToIdleList(&mActiveList[i]); else { // update poll flags mPollList[i+1].in_flags = in_flags; mPollList[i+1].out_flags = 0; } } } for (i=count-1; i>=0; --i) { //--- LOG((" idle [%u] { handler=%x condition=%x pollflags=%hu }\n", i, mIdleList[i].mHandler, mIdleList[i].mHandler->mCondition, mIdleList[i].mHandler->mPollFlags)); //--- if (NS_FAILED(mIdleList[i].mHandler->mCondition)) DetachSocket(&mIdleList[i]); else if (mIdleList[i].mHandler->mPollFlags != 0) MoveToPollList(&mIdleList[i]); } LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount)); // Measures seconds spent while blocked on PR_Poll PRUint32 pollInterval; PRInt32 n = Poll(&pollInterval); if (n < 0) { LOG((" PR_Poll error [%d]\n", PR_GetError())); active = PR_FALSE; } else { // // service "active" sockets... // for (i=0; i<PRInt32(mActiveCount); ++i) { PRPollDesc &desc = mPollList[i+1]; SocketContext &s = mActiveList[i]; if (n > 0 && desc.out_flags != 0) { s.mElapsedTime = 0; s.mHandler->OnSocketReady(desc.fd, desc.out_flags); } // check for timeout errors unless disabled... else if (s.mHandler->mPollTimeout != PR_UINT16_MAX) { // update elapsed time counter if (NS_UNLIKELY(pollInterval > (PR_UINT16_MAX - s.mElapsedTime))) s.mElapsedTime = PR_UINT16_MAX; else s.mElapsedTime += PRUint16(pollInterval); // check for timeout expiration if (s.mElapsedTime >= s.mHandler->mPollTimeout) { s.mElapsedTime = 0; s.mHandler->OnSocketReady(desc.fd, -1); } } } // // check for "dead" sockets and remove them (need to do this in // reverse order obviously). // for (i=mActiveCount-1; i>=0; --i) { if (NS_FAILED(mActiveList[i].mHandler->mCondition)) DetachSocket(&mActiveList[i]); } // // service the event queue (mPollList[0].fd == mThreadEvent) // if (n == 0) active = ServiceEventQ(); else if (mPollList[0].out_flags == PR_POLL_READ) { // acknowledge pollable event (wait should not block) if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) { // On Windows, the TCP loopback connection in the // pollable event may become broken when a laptop // switches between wired and wireless networks or // wakes up from hibernation. We try to create a // new pollable event. If that fails, we fall back // on "busy wait". { nsAutoLock lock(mEventQLock); PR_DestroyPollableEvent(mThreadEvent); mThreadEvent = PR_NewPollableEvent(); } if (!mThreadEvent) { NS_WARNING("running socket transport thread without " "a pollable event"); LOG(("running socket transport thread without " "a pollable event")); } mPollList[0].fd = mThreadEvent; // mPollList[0].in_flags was already set to PR_POLL_READ // at the beginning of this method. mPollList[0].out_flags = 0; } active = ServiceEventQ(); } } } // // shutdown thread // LOG(("shutting down socket transport thread...\n")); mShuttingDown = PR_TRUE; // detach any sockets for (i=mActiveCount-1; i>=0; --i) DetachSocket(&mActiveList[i]); for (i=mIdleCount-1; i>=0; --i) DetachSocket(&mIdleList[i]); mShuttingDown = PR_FALSE; // Final pass over the event queue. This makes sure that events posted by // socket detach handlers get processed. ServiceEventQ(); gSocketThread = nsnull; return NS_OK; }