12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523 |
- /* 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 "nsSocketTransportService2.h"
- #include "nsSocketTransport2.h"
- #include "NetworkActivityMonitor.h"
- #include "mozilla/Preferences.h"
- #include "nsIOService.h"
- #include "nsASocketHandler.h"
- #include "nsError.h"
- #include "prnetdb.h"
- #include "prerror.h"
- #include "nsIPrefService.h"
- #include "nsIPrefBranch.h"
- #include "nsServiceManagerUtils.h"
- #include "nsIObserverService.h"
- #include "mozilla/Atomics.h"
- #include "mozilla/Services.h"
- #include "mozilla/Likely.h"
- #include "mozilla/PublicSSL.h"
- #include "mozilla/ChaosMode.h"
- #include "mozilla/PodOperations.h"
- #include "nsThreadUtils.h"
- #include "nsIFile.h"
- #include "nsIWidget.h"
- namespace mozilla {
- namespace net {
- LazyLogModule gSocketTransportLog("nsSocketTransport");
- LazyLogModule gUDPSocketLog("UDPSocket");
- LazyLogModule gTCPSocketLog("TCPSocket");
- nsSocketTransportService *gSocketTransportService = nullptr;
- Atomic<PRThread*, Relaxed> gSocketThread;
- #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
- #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
- #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
- #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
- #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
- #define SOCKET_LIMIT_TARGET 1000U
- #define SOCKET_LIMIT_MIN 50U
- #define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
- #define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
- #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN "network.sts.max_time_for_pr_close_during_shutdown"
- #define REPAIR_POLLABLE_EVENT_TIME 10
- uint32_t nsSocketTransportService::gMaxCount;
- PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
- //-----------------------------------------------------------------------------
- bool
- nsSocketTransportService::SocketContext::IsTimedOut(PRIntervalTime now) const
- {
- return TimeoutIn(now) == 0;
- }
- void
- nsSocketTransportService::SocketContext::StartTimeout(PRIntervalTime now)
- {
- if (!mPollStartEpoch) {
- mPollStartEpoch = now;
- }
- }
- void
- nsSocketTransportService::SocketContext::StopTimeout()
- {
- mPollStartEpoch = 0;
- }
- void
- nsSocketTransportService::SocketContext::ResetTimeout()
- {
- if (mPollStartEpoch && mHandler->mPollTimeout == UINT16_MAX) {
- mPollStartEpoch = 0;
- }
- }
- PRIntervalTime
- nsSocketTransportService::SocketContext::TimeoutIn(PRIntervalTime now) const
- {
- if (mHandler->mPollTimeout == UINT16_MAX || !mPollStartEpoch) {
- return NS_SOCKET_POLL_TIMEOUT;
- }
- PRIntervalTime elapsed = (now - mPollStartEpoch);
- PRIntervalTime timeout = PR_SecondsToInterval(mHandler->mPollTimeout);
- if (elapsed >= timeout) {
- return 0;
- }
- return timeout - elapsed;
- }
- //-----------------------------------------------------------------------------
- // ctor/dtor (called on the main/UI thread by the service manager)
- nsSocketTransportService::nsSocketTransportService()
- : mThread(nullptr)
- , mLock("nsSocketTransportService::mLock")
- , mInitialized(false)
- , mShuttingDown(false)
- , mOffline(false)
- , mGoingOffline(false)
- , mRawThread(nullptr)
- , mActiveListSize(SOCKET_LIMIT_MIN)
- , mIdleListSize(SOCKET_LIMIT_MIN)
- , mActiveCount(0)
- , mIdleCount(0)
- , mSentBytesCount(0)
- , mReceivedBytesCount(0)
- , mSendBufferSize(0)
- , mKeepaliveIdleTimeS(600)
- , mKeepaliveRetryIntervalS(1)
- , mKeepaliveProbeCount(kDefaultTCPKeepCount)
- , mKeepaliveEnabledPref(false)
- , mServingPendingQueue(false)
- , mMaxTimePerPollIter(100)
- , mMaxTimeForPrClosePref(PR_SecondsToInterval(5))
- , mSleepPhase(false)
- , mProbedMaxCount(false)
- #if defined(XP_WIN)
- , mPolling(false)
- #endif
- {
- NS_ASSERTION(NS_IsMainThread(), "wrong thread");
- PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
- mActiveList = (SocketContext *)
- moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
- mIdleList = (SocketContext *)
- moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
- mPollList = (PRPollDesc *)
- moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1));
- NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
- gSocketTransportService = this;
- }
- nsSocketTransportService::~nsSocketTransportService()
- {
- NS_ASSERTION(NS_IsMainThread(), "wrong thread");
- NS_ASSERTION(!mInitialized, "not shutdown properly");
- free(mActiveList);
- free(mIdleList);
- free(mPollList);
- gSocketTransportService = nullptr;
- }
- //-----------------------------------------------------------------------------
- // event queue (any thread)
- already_AddRefed<nsIThread>
- nsSocketTransportService::GetThreadSafely()
- {
- MutexAutoLock lock(mLock);
- nsCOMPtr<nsIThread> result = mThread;
- return result.forget();
- }
- NS_IMETHODIMP
- nsSocketTransportService::DispatchFromScript(nsIRunnable *event, uint32_t flags)
- {
- nsCOMPtr<nsIRunnable> event_ref(event);
- return Dispatch(event_ref.forget(), flags);
- }
- NS_IMETHODIMP
- nsSocketTransportService::Dispatch(already_AddRefed<nsIRunnable> event, uint32_t flags)
- {
- nsCOMPtr<nsIRunnable> event_ref(event);
- SOCKET_LOG(("STS dispatch [%p]\n", event_ref.get()));
- nsCOMPtr<nsIThread> thread = GetThreadSafely();
- nsresult rv;
- rv = thread ? thread->Dispatch(event_ref.forget(), flags) : NS_ERROR_NOT_INITIALIZED;
- if (rv == NS_ERROR_UNEXPECTED) {
- // Thread is no longer accepting events. We must have just shut it
- // down on the main thread. Pretend we never saw it.
- rv = NS_ERROR_NOT_INITIALIZED;
- }
- return rv;
- }
- NS_IMETHODIMP
- nsSocketTransportService::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
- {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP
- nsSocketTransportService::IsOnCurrentThread(bool *result)
- {
- nsCOMPtr<nsIThread> thread = GetThreadSafely();
- NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
- return thread->IsOnCurrentThread(result);
- }
- //-----------------------------------------------------------------------------
- // socket api (socket thread only)
- NS_IMETHODIMP
- nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
- {
- SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
- NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
- if (CanAttachSocket()) {
- return Dispatch(event, NS_DISPATCH_NORMAL);
- }
- auto *runnable = new LinkedRunnableEvent(event);
- mPendingSocketQueue.insertBack(runnable);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
- {
- SOCKET_LOG(("nsSocketTransportService::AttachSocket [handler=%p]\n", handler));
- NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
- if (!CanAttachSocket()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- SocketContext sock;
- sock.mFD = fd;
- sock.mHandler = handler;
- sock.mPollStartEpoch = 0;
- nsresult rv = AddToIdleList(&sock);
- if (NS_SUCCEEDED(rv))
- NS_ADDREF(handler);
- return rv;
- }
- // the number of sockets that can be attached at any given time is
- // limited. this is done because some operating systems (e.g., Win9x)
- // limit the number of sockets that can be created by an application.
- // AttachSocket will fail if the limit is exceeded. consumers should
- // call CanAttachSocket and check the result before creating a socket.
- bool
- nsSocketTransportService::CanAttachSocket()
- {
- uint32_t total = mActiveCount + mIdleCount;
- bool rv = total < gMaxCount;
- return rv;
- }
- nsresult
- nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock)
- {
- SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", sock->mHandler));
- MOZ_ASSERT((listHead == mActiveList) || (listHead == mIdleList),
- "DetachSocket invalid head");
- // inform the handler that this socket is going away
- sock->mHandler->OnSocketDetached(sock->mFD);
- mSentBytesCount += sock->mHandler->ByteCountSent();
- mReceivedBytesCount += sock->mHandler->ByteCountReceived();
- // cleanup
- sock->mFD = nullptr;
- NS_RELEASE(sock->mHandler);
- if (listHead == mActiveList)
- RemoveFromPollList(sock);
- else
- RemoveFromIdleList(sock);
- // NOTE: sock is now an invalid pointer
-
- //
- // notify the first element on the pending socket queue...
- //
- nsCOMPtr<nsIRunnable> event;
- LinkedRunnableEvent *runnable = mPendingSocketQueue.getFirst();
- if (runnable) {
- event = runnable->TakeEvent();
- runnable->remove();
- delete runnable;
- }
- if (event) {
- // move event from pending queue to dispatch queue
- return Dispatch(event, NS_DISPATCH_NORMAL);
- }
- return NS_OK;
- }
- nsresult
- nsSocketTransportService::AddToPollList(SocketContext *sock)
- {
- MOZ_ASSERT(!(static_cast<uint32_t>(sock - mActiveList) < mActiveListSize),
- "AddToPollList Socket Already Active");
- SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%p]\n", sock->mHandler));
- if (mActiveCount == mActiveListSize) {
- SOCKET_LOG((" Active List size of %d met\n", mActiveCount));
- if (!GrowActiveList()) {
- NS_ERROR("too many active sockets");
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
-
- uint32_t newSocketIndex = mActiveCount;
- if (ChaosMode::isActive(ChaosFeature::NetworkScheduling)) {
- newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1);
- PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex,
- mActiveCount - newSocketIndex);
- PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1,
- mActiveCount - newSocketIndex);
- }
-
- sock->StartTimeout(PR_IntervalNow());
- mActiveList[newSocketIndex] = *sock;
- mActiveCount++;
- mPollList[newSocketIndex + 1].fd = sock->mFD;
- mPollList[newSocketIndex + 1].in_flags = sock->mHandler->mPollFlags;
- mPollList[newSocketIndex + 1].out_flags = 0;
- SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
- return NS_OK;
- }
- void
- nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
- {
- SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%p]\n", sock->mHandler));
- uint32_t index = sock - mActiveList;
- MOZ_ASSERT(index < mActiveListSize, "invalid index");
- SOCKET_LOG((" index=%u mActiveCount=%u\n", index, mActiveCount));
- if (index != mActiveCount-1) {
- mActiveList[index] = mActiveList[mActiveCount-1];
- mPollList[index+1] = mPollList[mActiveCount];
- }
- mActiveCount--;
- SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
- }
- nsresult
- nsSocketTransportService::AddToIdleList(SocketContext *sock)
- {
- MOZ_ASSERT(!(static_cast<uint32_t>(sock - mIdleList) < mIdleListSize),
- "AddToIdlelList Socket Already Idle");
- SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%p]\n", sock->mHandler));
- if (mIdleCount == mIdleListSize) {
- SOCKET_LOG((" Idle List size of %d met\n", mIdleCount));
- if (!GrowIdleList()) {
- NS_ERROR("too many idle sockets");
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- mIdleList[mIdleCount] = *sock;
- mIdleCount++;
- SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
- return NS_OK;
- }
- void
- nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
- {
- SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n", sock->mHandler));
- uint32_t index = sock - mIdleList;
- NS_ASSERTION(index < mIdleListSize, "invalid index in idle list");
- if (index != mIdleCount-1)
- mIdleList[index] = mIdleList[mIdleCount-1];
- mIdleCount--;
- SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
- }
- void
- nsSocketTransportService::MoveToIdleList(SocketContext *sock)
- {
- nsresult rv = AddToIdleList(sock);
- if (NS_FAILED(rv))
- DetachSocket(mActiveList, sock);
- else
- RemoveFromPollList(sock);
- }
- void
- nsSocketTransportService::MoveToPollList(SocketContext *sock)
- {
- nsresult rv = AddToPollList(sock);
- if (NS_FAILED(rv))
- DetachSocket(mIdleList, sock);
- else
- RemoveFromIdleList(sock);
- }
- bool
- nsSocketTransportService::GrowActiveList()
- {
- int32_t toAdd = gMaxCount - mActiveListSize;
- if (toAdd > 100) {
- toAdd = 100;
- } else if (toAdd < 1) {
- MOZ_ASSERT(false, "CanAttachSocket() should prevent this");
- return false;
- }
- mActiveListSize += toAdd;
- mActiveList = (SocketContext *)
- moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize);
- mPollList = (PRPollDesc *)
- moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
- return true;
- }
- bool
- nsSocketTransportService::GrowIdleList()
- {
- int32_t toAdd = gMaxCount - mIdleListSize;
- if (toAdd > 100) {
- toAdd = 100;
- } else if (toAdd < 1) {
- MOZ_ASSERT(false, "CanAttachSocket() should prevent this");
- return false;
- }
- mIdleListSize += toAdd;
- mIdleList = (SocketContext *)
- moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize);
- return true;
- }
- PRIntervalTime
- nsSocketTransportService::PollTimeout(PRIntervalTime now)
- {
- if (mActiveCount == 0) {
- return NS_SOCKET_POLL_TIMEOUT;
- }
- // compute minimum time before any socket timeout expires.
- PRIntervalTime minR = NS_SOCKET_POLL_TIMEOUT;
- for (uint32_t i=0; i<mActiveCount; ++i) {
- const SocketContext &s = mActiveList[i];
- PRIntervalTime r = s.TimeoutIn(now);
- if (r < minR) {
- minR = r;
- }
- }
-
- if (minR == NS_SOCKET_POLL_TIMEOUT) {
- SOCKET_LOG(("poll timeout: none\n"));
- return NS_SOCKET_POLL_TIMEOUT;
- }
- SOCKET_LOG(("poll timeout: %lu seconds\n", PR_IntervalToSeconds(minR)));
- return minR;
- }
- int32_t
- nsSocketTransportService::Poll(PRIntervalTime ts)
- {
- PRPollDesc *pollList;
- uint32_t pollCount;
- PRIntervalTime pollTimeout;
- // If there are pending events for this thread then
- // DoPollIteration() should service the network without blocking.
- bool pendingEvents = false;
- mRawThread->HasPendingEvents(&pendingEvents);
-
- if (mPollList[0].fd) {
- mPollList[0].out_flags = 0;
- pollList = mPollList;
- pollCount = mActiveCount + 1;
- pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout(ts);
- }
- else {
- // no pollable event, so busy wait...
- pollCount = mActiveCount;
- if (pollCount)
- pollList = &mPollList[1];
- else
- pollList = nullptr;
- pollTimeout =
- pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25);
- }
- SOCKET_LOG((" timeout = %i milliseconds\n",
- PR_IntervalToMilliseconds(pollTimeout)));
- int32_t rv = PR_Poll(pollList, pollCount, pollTimeout);
- SOCKET_LOG((" ...returned after %i milliseconds\n",
- PR_IntervalToMilliseconds(PR_IntervalNow() - ts)));
- return rv;
- }
- //-----------------------------------------------------------------------------
- // xpcom api
- NS_IMPL_ISUPPORTS(nsSocketTransportService,
- nsISocketTransportService,
- nsIRoutedSocketTransportService,
- nsIEventTarget,
- nsIThreadObserver,
- nsIRunnable,
- nsPISocketTransportService,
- nsIObserver)
- // called from main thread only
- NS_IMETHODIMP
- nsSocketTransportService::Init()
- {
- if (!NS_IsMainThread()) {
- NS_ERROR("wrong thread");
- return NS_ERROR_UNEXPECTED;
- }
- if (mInitialized)
- return NS_OK;
- if (mShuttingDown)
- return NS_ERROR_UNEXPECTED;
- nsCOMPtr<nsIThread> thread;
- nsresult rv = NS_NewNamedThread("Socket Thread", getter_AddRefs(thread), this);
- if (NS_FAILED(rv)) return rv;
-
- {
- MutexAutoLock lock(mLock);
- // Install our mThread, protecting against concurrent readers
- thread.swap(mThread);
- }
- nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (tmpPrefService) {
- tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
- tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false);
- tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false);
- tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false);
- tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false);
- tmpPrefService->AddObserver(MAX_TIME_BETWEEN_TWO_POLLS, this, false);
- tmpPrefService->AddObserver(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN, this, false);
- }
- UpdatePrefs();
- nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
- if (obsSvc) {
- obsSvc->AddObserver(this, "profile-initial-state", false);
- obsSvc->AddObserver(this, "last-pb-context-exited", false);
- obsSvc->AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC, true);
- obsSvc->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
- obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
- }
- mInitialized = true;
- return NS_OK;
- }
- // called from main thread only
- NS_IMETHODIMP
- nsSocketTransportService::Shutdown(bool aXpcomShutdown)
- {
- SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
- NS_ENSURE_STATE(NS_IsMainThread());
- if (!mInitialized)
- return NS_OK;
- if (mShuttingDown)
- return NS_ERROR_UNEXPECTED;
- {
- MutexAutoLock lock(mLock);
- // signal the socket thread to shutdown
- mShuttingDown = true;
- if (mPollableEvent) {
- mPollableEvent->Signal();
- }
- }
- if (!aXpcomShutdown) {
- return ShutdownThread();
- }
- return NS_OK;
- }
- nsresult
- nsSocketTransportService::ShutdownThread()
- {
- SOCKET_LOG(("nsSocketTransportService::ShutdownThread\n"));
- NS_ENSURE_STATE(NS_IsMainThread());
- if (!mInitialized || !mShuttingDown)
- return NS_OK;
- // join with thread
- mThread->Shutdown();
- {
- MutexAutoLock lock(mLock);
- // Drop our reference to mThread and make sure that any concurrent
- // readers are excluded
- mThread = nullptr;
- }
- nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (tmpPrefService)
- tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this);
- nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
- if (obsSvc) {
- obsSvc->RemoveObserver(this, "profile-initial-state");
- obsSvc->RemoveObserver(this, "last-pb-context-exited");
- obsSvc->RemoveObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC);
- obsSvc->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
- obsSvc->RemoveObserver(this, "xpcom-shutdown-threads");
- }
- if (mAfterWakeUpTimer) {
- mAfterWakeUpTimer->Cancel();
- mAfterWakeUpTimer = nullptr;
- }
- NetworkActivityMonitor::Shutdown();
- mInitialized = false;
- mShuttingDown = false;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::GetOffline(bool *offline)
- {
- *offline = mOffline;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::SetOffline(bool offline)
- {
- MutexAutoLock lock(mLock);
- if (!mOffline && offline) {
- // signal the socket thread to go offline, so it will detach sockets
- mGoingOffline = true;
- mOffline = true;
- }
- else if (mOffline && !offline) {
- mOffline = false;
- }
- if (mPollableEvent) {
- mPollableEvent->Signal();
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS)
- {
- MOZ_ASSERT(aKeepaliveIdleTimeS);
- if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
- return NS_ERROR_NULL_POINTER;
- }
- *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::GetKeepaliveRetryInterval(int32_t *aKeepaliveRetryIntervalS)
- {
- MOZ_ASSERT(aKeepaliveRetryIntervalS);
- if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
- return NS_ERROR_NULL_POINTER;
- }
- *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::GetKeepaliveProbeCount(int32_t *aKeepaliveProbeCount)
- {
- MOZ_ASSERT(aKeepaliveProbeCount);
- if (NS_WARN_IF(!aKeepaliveProbeCount)) {
- return NS_ERROR_NULL_POINTER;
- }
- *aKeepaliveProbeCount = mKeepaliveProbeCount;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::CreateTransport(const char **types,
- uint32_t typeCount,
- const nsACString &host,
- int32_t port,
- nsIProxyInfo *proxyInfo,
- nsISocketTransport **result)
- {
- return CreateRoutedTransport(types, typeCount, host, port, NS_LITERAL_CSTRING(""), 0,
- proxyInfo, result);
- }
- NS_IMETHODIMP
- nsSocketTransportService::CreateRoutedTransport(const char **types,
- uint32_t typeCount,
- const nsACString &host,
- int32_t port,
- const nsACString &hostRoute,
- int32_t portRoute,
- nsIProxyInfo *proxyInfo,
- nsISocketTransport **result)
- {
- NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
- NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
- RefPtr<nsSocketTransport> trans = new nsSocketTransport();
- nsresult rv = trans->Init(types, typeCount, host, port, hostRoute, portRoute, proxyInfo);
- if (NS_FAILED(rv)) {
- return rv;
- }
- trans.forget(result);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::CreateUnixDomainTransport(nsIFile *aPath,
- nsISocketTransport **result)
- {
- nsresult rv;
- NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
- nsAutoCString path;
- rv = aPath->GetNativePath(path);
- if (NS_FAILED(rv))
- return rv;
- RefPtr<nsSocketTransport> trans = new nsSocketTransport();
- rv = trans->InitWithFilename(path.get());
- if (NS_FAILED(rv))
- return rv;
- trans.forget(result);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
- {
- #ifndef XP_WIN
- // On windows poll can hang and this became worse when we introduced the
- // patch for bug 698882 (see also bug 1292181), therefore we reverted the
- // behavior on windows to be as before bug 698882, e.g. write to the socket
- // also if an event dispatch is on the socket thread and writing to the
- // socket for each event.
- if (PR_GetCurrentThread() == gSocketThread) {
- // this check is redundant to one done inside ::Signal(), but
- // we can do it here and skip obtaining the lock - given that
- // this is a relatively common occurance its worth the
- // redundant code
- SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
- return NS_OK;
- }
- #else
- if (gIOService->IsNetTearingDown()) {
- // Poll can hang sometimes. If we are in shutdown, we are going to
- // start a watchdog. If we do not exit poll within
- // REPAIR_POLLABLE_EVENT_TIME signal a pollable event again.
- StartPollWatchdog();
- }
- #endif
- MutexAutoLock lock(mLock);
- if (mPollableEvent) {
- mPollableEvent->Signal();
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
- bool mayWait)
- {
- return NS_OK;
- }
- NS_IMETHODIMP
- nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
- bool eventWasProcessed)
- {
- return NS_OK;
- }
- void
- nsSocketTransportService::MarkTheLastElementOfPendingQueue()
- {
- mServingPendingQueue = false;
- }
- NS_IMETHODIMP
- nsSocketTransportService::Run()
- {
- SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount));
- psm::InitializeSSLServerCertVerificationThreads();
- gSocketThread = PR_GetCurrentThread();
- {
- MutexAutoLock 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 (;;) {
- bool pendingEvents = false;
- do {
- DoPollIteration();
- 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;
- }
- }
- TimeStamp eventQueueStart = TimeStamp::NowLoRes();
- do {
- NS_ProcessNextEvent(mRawThread);
- pendingEvents = false;
- mRawThread->HasPendingEvents(&pendingEvents);
- } while (pendingEvents && mServingPendingQueue &&
- ((TimeStamp::NowLoRes() -
- eventQueueStart).ToMilliseconds() <
- mMaxTimePerPollIter));
- }
- } while (pendingEvents);
- bool goingOffline = false;
- // now that our event queue is empty, check to see if we should exit
- {
- MutexAutoLock lock(mLock);
- if (mShuttingDown) {
- 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
- nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals,
- SocketContext *socketList,
- int32_t index)
- {
- bool isGuarded = false;
- if (aGuardLocals) {
- socketList[index].mHandler->IsLocal(&isGuarded);
- if (!isGuarded)
- socketList[index].mHandler->KeepWhenOffline(&isGuarded);
- }
- if (!isGuarded)
- DetachSocket(socketList, &socketList[index]);
- }
- void
- nsSocketTransportService::Reset(bool aGuardLocals)
- {
- // detach any sockets
- int32_t i;
- for (i = mActiveCount - 1; i >= 0; --i) {
- DetachSocketWithGuard(aGuardLocals, mActiveList, i);
- }
- for (i = mIdleCount - 1; i >= 0; --i) {
- DetachSocketWithGuard(aGuardLocals, mIdleList, i);
- }
- }
- nsresult
- nsSocketTransportService::DoPollIteration()
- {
- SOCKET_LOG(("STS poll iter\n"));
- // Freeze "now" for list updates and polling.
- PRIntervalTime now = PR_IntervalNow();
-
- 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;
- // Active polling entry; start timeout.
- mActiveList[i].StartTimeout(now);
- }
- }
- }
- 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
- int32_t n = 0;
-
- if (!gIOService->IsNetTearingDown()) {
- // Let's not do polling during shutdown.
- #if defined(XP_WIN)
- StartPolling();
- #endif
- n = Poll(now);
- #if defined(XP_WIN)
- EndPolling();
- #endif
- }
-
- // Refresh when "now" is for following checks.
- now = PR_IntervalNow();
- if (n < 0) {
- SOCKET_LOG((" PR_Poll error [%d] os error [%d]\n", PR_GetError(),
- PR_GetOSError()));
- } else {
- //
- // service "active" sockets...
- //
- 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.StopTimeout();
- s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
- } else if (s.IsTimedOut(now)) {
- // Socket timed out; disengage.
- s.StopTimeout();
- s.mHandler->OnSocketReady(desc.fd, -1);
- } else {
- // We may have recorded a timeout start on a socket and subsequently
- // set it to not time out. Check the socket and reset the timestamp
- // in this case to keep our states predictable.
- s.ResetTimeout();
- }
- }
- //
- // 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;
- }
- void
- nsSocketTransportService::UpdateSendBufferPref(nsIPrefBranch *pref)
- {
- int32_t bufferSize;
- // If the pref is set, honor it. 0 means use OS defaults.
- nsresult rv = pref->GetIntPref(SEND_BUFFER_PREF, &bufferSize);
- if (NS_SUCCEEDED(rv)) {
- mSendBufferSize = bufferSize;
- return;
- }
- #if defined(XP_WIN)
- mSendBufferSize = 131072 * 4;
- #endif
- }
- nsresult
- nsSocketTransportService::UpdatePrefs()
- {
- mSendBufferSize = 0;
- nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (tmpPrefService) {
- UpdateSendBufferPref(tmpPrefService);
- // Default TCP Keepalive Values.
- int32_t keepaliveIdleTimeS;
- nsresult rv = tmpPrefService->GetIntPref(KEEPALIVE_IDLE_TIME_PREF,
- &keepaliveIdleTimeS);
- if (NS_SUCCEEDED(rv))
- mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS,
- 1, kMaxTCPKeepIdle);
- int32_t keepaliveRetryIntervalS;
- rv = tmpPrefService->GetIntPref(KEEPALIVE_RETRY_INTERVAL_PREF,
- &keepaliveRetryIntervalS);
- if (NS_SUCCEEDED(rv))
- mKeepaliveRetryIntervalS = clamped(keepaliveRetryIntervalS,
- 1, kMaxTCPKeepIntvl);
- int32_t keepaliveProbeCount;
- rv = tmpPrefService->GetIntPref(KEEPALIVE_PROBE_COUNT_PREF,
- &keepaliveProbeCount);
- if (NS_SUCCEEDED(rv))
- mKeepaliveProbeCount = clamped(keepaliveProbeCount,
- 1, kMaxTCPKeepCount);
- bool keepaliveEnabled = false;
- rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF,
- &keepaliveEnabled);
- if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
- mKeepaliveEnabledPref = keepaliveEnabled;
- OnKeepaliveEnabledPrefChange();
- }
- int32_t maxTimePref;
- rv = tmpPrefService->GetIntPref(MAX_TIME_BETWEEN_TWO_POLLS,
- &maxTimePref);
- if (NS_SUCCEEDED(rv) && maxTimePref >= 0) {
- mMaxTimePerPollIter = maxTimePref;
- }
- int32_t maxTimeForPrClosePref;
- rv = tmpPrefService->GetIntPref(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN,
- &maxTimeForPrClosePref);
- if (NS_SUCCEEDED(rv) && maxTimeForPrClosePref >=0) {
- mMaxTimeForPrClosePref = PR_MillisecondsToInterval(maxTimeForPrClosePref);
- }
- }
- return NS_OK;
- }
- void
- nsSocketTransportService::OnKeepaliveEnabledPrefChange()
- {
- // Dispatch to socket thread if we're not executing there.
- if (PR_GetCurrentThread() != gSocketThread) {
- gSocketTransportService->Dispatch(
- NewRunnableMethod(
- this, &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
- NS_DISPATCH_NORMAL);
- return;
- }
- SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
- mKeepaliveEnabledPref ? "enabled" : "disabled"));
- // Notify each socket that keepalive has been en/disabled globally.
- for (int32_t i = mActiveCount - 1; i >= 0; --i) {
- NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
- }
- for (int32_t i = mIdleCount - 1; i >= 0; --i) {
- NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
- }
- }
- void
- nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock)
- {
- MOZ_ASSERT(sock, "SocketContext cannot be null!");
- MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
- if (!sock || !sock->mHandler) {
- return;
- }
- sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
- }
- NS_IMETHODIMP
- nsSocketTransportService::Observe(nsISupports *subject,
- const char *topic,
- const char16_t *data)
- {
- if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
- UpdatePrefs();
- return NS_OK;
- }
- if (!strcmp(topic, "profile-initial-state")) {
- int32_t blipInterval = Preferences::GetInt(BLIP_INTERVAL_PREF, 0);
- if (blipInterval <= 0) {
- return NS_OK;
- }
- return net::NetworkActivityMonitor::Init(blipInterval);
- }
- if (!strcmp(topic, "last-pb-context-exited")) {
- nsCOMPtr<nsIRunnable> ev =
- NewRunnableMethod(this,
- &nsSocketTransportService::ClosePrivateConnections);
- nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
- nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
- if (timer == mAfterWakeUpTimer) {
- mAfterWakeUpTimer = nullptr;
- mSleepPhase = false;
- }
- #if defined(XP_WIN)
- if (timer == mPollRepairTimer) {
- DoPollRepair();
- }
- #endif
- } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
- mSleepPhase = true;
- if (mAfterWakeUpTimer) {
- mAfterWakeUpTimer->Cancel();
- mAfterWakeUpTimer = nullptr;
- }
- } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
- if (mSleepPhase && !mAfterWakeUpTimer) {
- mAfterWakeUpTimer = do_CreateInstance("@mozilla.org/timer;1");
- if (mAfterWakeUpTimer) {
- mAfterWakeUpTimer->Init(this, 2000, nsITimer::TYPE_ONE_SHOT);
- }
- }
- } else if (!strcmp(topic, "xpcom-shutdown-threads")) {
- ShutdownThread();
- }
- return NS_OK;
- }
- void
- nsSocketTransportService::ClosePrivateConnections()
- {
- // Must be called on the socket thread.
- #ifdef DEBUG
- bool onSTSThread;
- IsOnCurrentThread(&onSTSThread);
- MOZ_ASSERT(onSTSThread);
- #endif
- for (int32_t i = mActiveCount - 1; i >= 0; --i) {
- if (mActiveList[i].mHandler->mIsPrivate) {
- DetachSocket(mActiveList, &mActiveList[i]);
- }
- }
- for (int32_t i = mIdleCount - 1; i >= 0; --i) {
- if (mIdleList[i].mHandler->mIsPrivate) {
- DetachSocket(mIdleList, &mIdleList[i]);
- }
- }
- ClearPrivateSSLState();
- }
- NS_IMETHODIMP
- nsSocketTransportService::GetSendBufferSize(int32_t *value)
- {
- *value = mSendBufferSize;
- return NS_OK;
- }
- /// ugly OS specific includes are placed at the bottom of the src for clarity
- #if defined(XP_WIN)
- #include <windows.h>
- #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
- #include <sys/resource.h>
- #endif
- // Right now the only need to do this is on windows.
- #if defined(XP_WIN)
- void
- nsSocketTransportService::ProbeMaxCount()
- {
- NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
- if (mProbedMaxCount)
- return;
- mProbedMaxCount = true;
- // Allocate and test a PR_Poll up to the gMaxCount number of unconnected
- // sockets. See bug 692260 - windows should be able to handle 1000 sockets
- // in select() without a problem, but LSPs have been known to balk at lower
- // numbers. (64 in the bug).
- // Allocate
- struct PRPollDesc pfd[SOCKET_LIMIT_TARGET];
- uint32_t numAllocated = 0;
- for (uint32_t index = 0 ; index < gMaxCount; ++index) {
- pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
- pfd[index].out_flags = 0;
- pfd[index].fd = PR_OpenTCPSocket(PR_AF_INET);
- if (!pfd[index].fd) {
- SOCKET_LOG(("Socket Limit Test index %d failed\n", index));
- if (index < SOCKET_LIMIT_MIN)
- gMaxCount = SOCKET_LIMIT_MIN;
- else
- gMaxCount = index;
- break;
- }
- ++numAllocated;
- }
- // Test
- static_assert(SOCKET_LIMIT_MIN >= 32U, "Minimum Socket Limit is >= 32");
- while (gMaxCount <= numAllocated) {
- int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0));
-
- SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n",
- gMaxCount, rv));
- if (rv >= 0)
- break;
- SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
- gMaxCount, rv, PR_GetError()));
- gMaxCount -= 32;
- if (gMaxCount <= SOCKET_LIMIT_MIN) {
- gMaxCount = SOCKET_LIMIT_MIN;
- break;
- }
- }
- // Free
- for (uint32_t index = 0 ; index < numAllocated; ++index)
- if (pfd[index].fd)
- PR_Close(pfd[index].fd);
- SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
- }
- #endif // windows
- PRStatus
- nsSocketTransportService::DiscoverMaxCount()
- {
- gMaxCount = SOCKET_LIMIT_MIN;
- #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
- // On unix and os x network sockets and file
- // descriptors are the same. OS X comes defaulted at 256,
- // most linux at 1000. We can reliably use [sg]rlimit to
- // query that and raise it if needed.
- struct rlimit rlimitData;
- if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) // rlimit broken - use min
- return PR_SUCCESS;
- if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET) { // larger than target!
- gMaxCount = SOCKET_LIMIT_TARGET;
- return PR_SUCCESS;
- }
- int32_t maxallowed = rlimitData.rlim_max;
- if ((uint32_t)maxallowed <= SOCKET_LIMIT_MIN) {
- return PR_SUCCESS; // so small treat as if rlimit is broken
- }
- if ((maxallowed == -1) || // no hard cap - ok to set target
- ((uint32_t)maxallowed >= SOCKET_LIMIT_TARGET)) {
- maxallowed = SOCKET_LIMIT_TARGET;
- }
- rlimitData.rlim_cur = maxallowed;
- setrlimit(RLIMIT_NOFILE, &rlimitData);
- if ((getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) &&
- (rlimitData.rlim_cur > SOCKET_LIMIT_MIN)) {
- gMaxCount = rlimitData.rlim_cur;
- }
- #elif defined(XP_WIN) && !defined(WIN_CE)
- // >= XP is confirmed to have at least 1000
- static_assert(SOCKET_LIMIT_TARGET <= 1000, "SOCKET_LIMIT_TARGET max value is 1000");
- gMaxCount = SOCKET_LIMIT_TARGET;
- #else
- // other platforms are harder to test - so leave at safe legacy value
- #endif
- return PR_SUCCESS;
- }
- // Used to return connection info to Dashboard.cpp
- void
- nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo> *data,
- struct SocketContext *context, bool aActive)
- {
- if (context->mHandler->mIsPrivate)
- return;
- PRFileDesc *aFD = context->mFD;
- PRFileDesc *idLayer = PR_GetIdentitiesLayer(aFD, PR_NSPR_IO_LAYER);
- NS_ENSURE_TRUE_VOID(idLayer);
- bool tcp = PR_GetDescType(idLayer) == PR_DESC_SOCKET_TCP;
- PRNetAddr peer_addr;
- PodZero(&peer_addr);
- PRStatus rv = PR_GetPeerName(aFD, &peer_addr);
- if (rv != PR_SUCCESS)
- return;
- char host[64] = {0};
- rv = PR_NetAddrToString(&peer_addr, host, sizeof(host));
- if (rv != PR_SUCCESS)
- return;
- uint16_t port;
- if (peer_addr.raw.family == PR_AF_INET)
- port = peer_addr.inet.port;
- else
- port = peer_addr.ipv6.port;
- port = PR_ntohs(port);
- uint64_t sent = context->mHandler->ByteCountSent();
- uint64_t received = context->mHandler->ByteCountReceived();
- SocketInfo info = { nsCString(host), sent, received, port, aActive, tcp };
- data->AppendElement(info);
- }
- void
- nsSocketTransportService::GetSocketConnections(nsTArray<SocketInfo> *data)
- {
- NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
- for (uint32_t i = 0; i < mActiveCount; i++)
- AnalyzeConnection(data, &mActiveList[i], true);
- for (uint32_t i = 0; i < mIdleCount; i++)
- AnalyzeConnection(data, &mIdleList[i], false);
- }
- #if defined(XP_WIN)
- void
- nsSocketTransportService::StartPollWatchdog()
- {
- MutexAutoLock lock(mLock);
- // Poll can hang sometimes. If we are in shutdown, we are going to start a
- // watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME
- // signal a pollable event again.
- MOZ_ASSERT(gIOService->IsNetTearingDown());
- if (mPolling && !mPollRepairTimer) {
- mPollRepairTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
- mPollRepairTimer->Init(this, REPAIR_POLLABLE_EVENT_TIME,
- nsITimer::TYPE_REPEATING_SLACK);
- }
- }
- void
- nsSocketTransportService::DoPollRepair()
- {
- MutexAutoLock lock(mLock);
- if (mPolling && mPollableEvent) {
- mPollableEvent->Signal();
- } else if (mPollRepairTimer) {
- mPollRepairTimer->Cancel();
- }
- }
- void
- nsSocketTransportService::StartPolling()
- {
- MutexAutoLock lock(mLock);
- mPolling = true;
- }
- void
- nsSocketTransportService::EndPolling()
- {
- MutexAutoLock lock(mLock);
- mPolling = false;
- if (mPollRepairTimer) {
- mPollRepairTimer->Cancel();
- }
- }
- #endif
- } // namespace net
- } // namespace mozilla
|