tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsSocketTransportService2.cpp (60094B)


      1 // vim:set sw=2 sts=2 et cin:
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsSocketTransportService2.h"
      7 
      8 #include "mozilla/Atomics.h"
      9 #include "mozilla/ChaosMode.h"
     10 #include "mozilla/glean/NetwerkMetrics.h"
     11 #include "mozilla/IntegerPrintfMacros.h"
     12 #include "mozilla/MaybeLeakRefPtr.h"
     13 #include "mozilla/PodOperations.h"
     14 #include "mozilla/Preferences.h"
     15 #include "mozilla/ProfilerMarkers.h"
     16 #include "mozilla/ProfilerThreadSleep.h"
     17 #include "mozilla/PublicSSL.h"
     18 #include "mozilla/ReverseIterator.h"
     19 #include "mozilla/Services.h"
     20 #include "mozilla/StaticPrefs_network.h"
     21 #include "mozilla/Tokenizer.h"
     22 #include "mozilla/Telemetry.h"
     23 #include "nsASocketHandler.h"
     24 #include "nsError.h"
     25 #include "nsIEventTarget.h"
     26 #include "nsIFile.h"
     27 #include "nsINetworkLinkService.h"
     28 #include "nsIOService.h"
     29 #include "nsIObserverService.h"
     30 #include "nsIWidget.h"
     31 #include "nsServiceManagerUtils.h"
     32 #include "nsSocketTransport2.h"
     33 #include "nsThreadUtils.h"
     34 #include "prerror.h"
     35 #include "prnetdb.h"
     36 #ifdef XP_UNIX
     37 #  include "private/pprio.h"
     38 #endif
     39 
     40 namespace mozilla {
     41 namespace net {
     42 
     43 #define SOCKET_THREAD_LONGTASK_MS 3
     44 
     45 LazyLogModule gSocketTransportLog("nsSocketTransport");
     46 LazyLogModule gUDPSocketLog("UDPSocket");
     47 LazyLogModule gTCPSocketLog("TCPSocket");
     48 
     49 nsSocketTransportService* gSocketTransportService = nullptr;
     50 static Atomic<PRThread*, Relaxed> gSocketThread(nullptr);
     51 
     52 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
     53 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
     54 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
     55 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
     56 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
     57 #define SOCKET_LIMIT_TARGET 1000U
     58 #define MAX_TIME_BETWEEN_TWO_POLLS \
     59  "network.sts.max_time_for_events_between_two_polls"
     60 #define POLL_BUSY_WAIT_PERIOD "network.sts.poll_busy_wait_period"
     61 #define POLL_BUSY_WAIT_PERIOD_TIMEOUT \
     62  "network.sts.poll_busy_wait_period_timeout"
     63 #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN \
     64  "network.sts.max_time_for_pr_close_during_shutdown"
     65 #define POLLABLE_EVENT_TIMEOUT "network.sts.pollable_event_timeout"
     66 
     67 #define REPAIR_POLLABLE_EVENT_TIME 10
     68 
     69 uint32_t nsSocketTransportService::gMaxCount;
     70 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
     71 
     72 // Utility functions
     73 bool OnSocketThread() { return PR_GetCurrentThread() == gSocketThread; }
     74 
     75 //-----------------------------------------------------------------------------
     76 
     77 bool nsSocketTransportService::SocketContext::IsTimedOut(
     78    PRIntervalTime now) const {
     79  return TimeoutIn(now) == 0;
     80 }
     81 
     82 void nsSocketTransportService::SocketContext::EnsureTimeout(
     83    PRIntervalTime now) {
     84  SOCKET_LOG(("SocketContext::EnsureTimeout socket=%p", mHandler.get()));
     85  if (!mPollStartEpoch) {
     86    SOCKET_LOG(("  engaging"));
     87    mPollStartEpoch = now;
     88  }
     89 }
     90 
     91 void nsSocketTransportService::SocketContext::DisengageTimeout() {
     92  SOCKET_LOG(("SocketContext::DisengageTimeout socket=%p", mHandler.get()));
     93  mPollStartEpoch = 0;
     94 }
     95 
     96 PRIntervalTime nsSocketTransportService::SocketContext::TimeoutIn(
     97    PRIntervalTime now) const {
     98  SOCKET_LOG(("SocketContext::TimeoutIn socket=%p, timeout=%us", mHandler.get(),
     99              mHandler->mPollTimeout));
    100 
    101  if (mHandler->mPollTimeout == UINT16_MAX || !mPollStartEpoch) {
    102    SOCKET_LOG(("  not engaged"));
    103    return NS_SOCKET_POLL_TIMEOUT;
    104  }
    105 
    106  PRIntervalTime elapsed = (now - mPollStartEpoch);
    107  PRIntervalTime timeout = PR_SecondsToInterval(mHandler->mPollTimeout);
    108 
    109  if (elapsed >= timeout) {
    110    SOCKET_LOG(("  timed out!"));
    111    return 0;
    112  }
    113  SOCKET_LOG(("  remains %us", PR_IntervalToSeconds(timeout - elapsed)));
    114  return timeout - elapsed;
    115 }
    116 
    117 void nsSocketTransportService::SocketContext::MaybeResetEpoch() {
    118  if (mPollStartEpoch && mHandler->mPollTimeout == UINT16_MAX) {
    119    mPollStartEpoch = 0;
    120  }
    121 }
    122 
    123 //-----------------------------------------------------------------------------
    124 // ctor/dtor (called on the main/UI thread by the service manager)
    125 
    126 nsSocketTransportService::nsSocketTransportService()
    127    : mPollableEventTimeout(TimeDuration::FromSeconds(6)),
    128      mMaxTimeForPrClosePref(PR_SecondsToInterval(5)),
    129      mNetworkLinkChangeBusyWaitPeriod(PR_SecondsToInterval(50)),
    130      mNetworkLinkChangeBusyWaitTimeout(PR_SecondsToInterval(7)) {
    131  NS_ASSERTION(NS_IsMainThread(), "wrong thread");
    132 
    133  PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
    134 
    135  NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
    136  gSocketTransportService = this;
    137 
    138  // The Poll list always has an entry at [0].   The rest of the
    139  // list is a duplicate of the Active list's PRFileDesc file descriptors.
    140  PRPollDesc entry = {nullptr, PR_POLL_READ | PR_POLL_EXCEPT, 0};
    141  mPollList.InsertElementAt(0, entry);
    142 }
    143 
    144 void nsSocketTransportService::ApplyPortRemap(uint16_t* aPort) {
    145  MOZ_ASSERT(IsOnCurrentThreadInfallible());
    146 
    147  if (!mPortRemapping) {
    148    return;
    149  }
    150 
    151  // Reverse the array to make later rules override earlier rules.
    152  for (auto const& portMapping : Reversed(*mPortRemapping)) {
    153    if (*aPort < std::get<0>(portMapping)) {
    154      continue;
    155    }
    156    if (*aPort > std::get<1>(portMapping)) {
    157      continue;
    158    }
    159 
    160    *aPort = std::get<2>(portMapping);
    161    return;
    162  }
    163 }
    164 
    165 bool nsSocketTransportService::UpdatePortRemapPreference(
    166    nsACString const& aPortMappingPref) {
    167  TPortRemapping portRemapping;
    168 
    169  auto consumePreference = [&]() -> bool {
    170    Tokenizer tokenizer(aPortMappingPref);
    171 
    172    tokenizer.SkipWhites();
    173    if (tokenizer.CheckEOF()) {
    174      return true;
    175    }
    176 
    177    nsTArray<std::tuple<uint16_t, uint16_t>> ranges(2);
    178    while (true) {
    179      uint16_t loPort;
    180      tokenizer.SkipWhites();
    181      if (!tokenizer.ReadInteger(&loPort)) {
    182        break;
    183      }
    184 
    185      uint16_t hiPort;
    186      tokenizer.SkipWhites();
    187      if (tokenizer.CheckChar('-')) {
    188        tokenizer.SkipWhites();
    189        if (!tokenizer.ReadInteger(&hiPort)) {
    190          break;
    191        }
    192      } else {
    193        hiPort = loPort;
    194      }
    195 
    196      ranges.AppendElement(std::make_tuple(loPort, hiPort));
    197 
    198      tokenizer.SkipWhites();
    199      if (tokenizer.CheckChar(',')) {
    200        continue;  // another port or port range is expected
    201      }
    202 
    203      if (tokenizer.CheckChar('=')) {
    204        uint16_t targetPort;
    205        tokenizer.SkipWhites();
    206        if (!tokenizer.ReadInteger(&targetPort)) {
    207          break;
    208        }
    209 
    210        // Storing reversed, because the most common cases (like 443) will very
    211        // likely be listed as first, less common cases will be added to the end
    212        // of the list mapping to the same port. As we iterate the whole
    213        // remapping array from the end, this may have a small perf win by
    214        // hitting the most common cases earlier.
    215        for (auto const& range : Reversed(ranges)) {
    216          portRemapping.AppendElement(std::make_tuple(
    217              std::get<0>(range), std::get<1>(range), targetPort));
    218        }
    219        ranges.Clear();
    220 
    221        tokenizer.SkipWhites();
    222        if (tokenizer.CheckChar(';')) {
    223          continue;  // more mappings (or EOF) expected
    224        }
    225        if (tokenizer.CheckEOF()) {
    226          return true;
    227        }
    228      }
    229 
    230      // Anything else is unexpected.
    231      break;
    232    }
    233 
    234    // 'break' from the parsing loop means ill-formed preference
    235    portRemapping.Clear();
    236    return false;
    237  };
    238 
    239  bool rv = consumePreference();
    240 
    241  if (!IsOnCurrentThread()) {
    242    nsCOMPtr<nsIThread> thread = GetThreadSafely();
    243    if (!thread) {
    244      // Init hasn't been called yet. Could probably just assert.
    245      // If shutdown, the dispatch below will just silently fail.
    246      NS_ASSERTION(false, "ApplyPortRemapPreference before STS::Init");
    247      return false;
    248    }
    249    thread->Dispatch(NewRunnableMethod<TPortRemapping>(
    250        "net::ApplyPortRemapping", this,
    251        &nsSocketTransportService::ApplyPortRemapPreference, portRemapping));
    252  } else {
    253    ApplyPortRemapPreference(portRemapping);
    254  }
    255 
    256  return rv;
    257 }
    258 
    259 nsSocketTransportService::~nsSocketTransportService() {
    260  NS_ASSERTION(NS_IsMainThread(), "wrong thread");
    261  NS_ASSERTION(!mInitialized, "not shutdown properly");
    262 
    263  gSocketTransportService = nullptr;
    264 }
    265 
    266 //-----------------------------------------------------------------------------
    267 // event queue (any thread)
    268 
    269 already_AddRefed<nsIThread> nsSocketTransportService::GetThreadSafely() {
    270  MutexAutoLock lock(mLock);
    271  nsCOMPtr<nsIThread> result = mThread;
    272  return result.forget();
    273 }
    274 
    275 NS_IMETHODIMP
    276 nsSocketTransportService::DispatchFromScript(nsIRunnable* event,
    277                                             DispatchFlags flags) {
    278  return Dispatch(do_AddRef(event), flags);
    279 }
    280 
    281 NS_IMETHODIMP
    282 nsSocketTransportService::Dispatch(already_AddRefed<nsIRunnable> event,
    283                                   DispatchFlags flags) {
    284  // NOTE: We don't leak runnables on dispatch failure here, even if
    285  // NS_DISPATCH_FALLIBLE is not specified.
    286  nsCOMPtr<nsIRunnable> event_ref(std::move(event));
    287  SOCKET_LOG(("STS dispatch [%p]\n", event_ref.get()));
    288 
    289  nsCOMPtr<nsIThread> thread = GetThreadSafely();
    290  nsresult rv = thread ? thread->Dispatch(event_ref.forget(),
    291                                          flags | NS_DISPATCH_FALLIBLE)
    292                       : NS_ERROR_NOT_INITIALIZED;
    293  if (rv == NS_ERROR_UNEXPECTED) {
    294    // Thread is no longer accepting events. We must have just shut it
    295    // down on the main thread. Pretend we never saw it.
    296    rv = NS_ERROR_NOT_INITIALIZED;
    297  }
    298  return rv;
    299 }
    300 
    301 NS_IMETHODIMP
    302 nsSocketTransportService::DelayedDispatch(already_AddRefed<nsIRunnable>,
    303                                          uint32_t) {
    304  return NS_ERROR_NOT_IMPLEMENTED;
    305 }
    306 
    307 NS_IMETHODIMP
    308 nsSocketTransportService::RegisterShutdownTask(nsITargetShutdownTask* task) {
    309  nsCOMPtr<nsIThread> thread = GetThreadSafely();
    310  return thread ? thread->RegisterShutdownTask(task) : NS_ERROR_UNEXPECTED;
    311 }
    312 
    313 NS_IMETHODIMP
    314 nsSocketTransportService::UnregisterShutdownTask(nsITargetShutdownTask* task) {
    315  nsCOMPtr<nsIThread> thread = GetThreadSafely();
    316  return thread ? thread->UnregisterShutdownTask(task) : NS_ERROR_UNEXPECTED;
    317 }
    318 
    319 NS_IMETHODIMP
    320 nsSocketTransportService::IsOnCurrentThread(bool* result) {
    321  *result = OnSocketThread();
    322  return NS_OK;
    323 }
    324 
    325 NS_IMETHODIMP_(bool)
    326 nsSocketTransportService::IsOnCurrentThreadInfallible() {
    327  return OnSocketThread();
    328 }
    329 
    330 //-----------------------------------------------------------------------------
    331 // nsIDirectTaskDispatcher
    332 
    333 already_AddRefed<nsIDirectTaskDispatcher>
    334 nsSocketTransportService::GetDirectTaskDispatcherSafely() {
    335  MutexAutoLock lock(mLock);
    336  nsCOMPtr<nsIDirectTaskDispatcher> result = mDirectTaskDispatcher;
    337  return result.forget();
    338 }
    339 
    340 NS_IMETHODIMP
    341 nsSocketTransportService::DispatchDirectTask(
    342    already_AddRefed<nsIRunnable> aEvent) {
    343  nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
    344      GetDirectTaskDispatcherSafely();
    345  NS_ENSURE_TRUE(dispatcher, NS_ERROR_NOT_INITIALIZED);
    346  return dispatcher->DispatchDirectTask(std::move(aEvent));
    347 }
    348 
    349 NS_IMETHODIMP nsSocketTransportService::DrainDirectTasks() {
    350  nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
    351      GetDirectTaskDispatcherSafely();
    352  if (!dispatcher) {
    353    // nothing to drain.
    354    return NS_OK;
    355  }
    356  return dispatcher->DrainDirectTasks();
    357 }
    358 
    359 NS_IMETHODIMP nsSocketTransportService::HaveDirectTasks(bool* aValue) {
    360  nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
    361      GetDirectTaskDispatcherSafely();
    362  if (!dispatcher) {
    363    *aValue = false;
    364    return NS_OK;
    365  }
    366  return dispatcher->HaveDirectTasks(aValue);
    367 }
    368 
    369 //-----------------------------------------------------------------------------
    370 // socket api (socket thread only)
    371 
    372 NS_IMETHODIMP
    373 nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable* event) {
    374  SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
    375 
    376  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    377 
    378  if (CanAttachSocket()) {
    379    return Dispatch(event, NS_DISPATCH_NORMAL);
    380  }
    381 
    382  auto* runnable = new LinkedRunnableEvent(event);
    383  mPendingSocketQueue.insertBack(runnable);
    384  return NS_OK;
    385 }
    386 
    387 NS_IMETHODIMP
    388 nsSocketTransportService::AttachSocket(PRFileDesc* fd,
    389                                       nsASocketHandler* handler) {
    390  SOCKET_LOG(
    391      ("nsSocketTransportService::AttachSocket [handler=%p]\n", handler));
    392 
    393 #ifdef XP_UNIX
    394 #  ifdef XP_DARWIN
    395  // See the Darwin case in config/external/nspr/pr/moz.build
    396  static constexpr PROsfd kFDs = 4096;
    397 #  else
    398  static constexpr PROsfd kFDs = 65536;
    399 #  endif
    400  PROsfd osfd = PR_FileDesc2NativeHandle(fd);
    401  // If the native fd exceeds what PR_Poll can handle, PR_Poll will treat it as
    402  // invalid (POLLNVAL) and networking degrades into hard-to-debug failures.
    403  // Crash early with a clear reason instead. See bug 1980171 for context.
    404  MOZ_RELEASE_ASSERT(osfd < kFDs);
    405 #endif
    406 
    407  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    408 
    409  if (!CanAttachSocket()) {
    410    return NS_ERROR_NOT_AVAILABLE;
    411  }
    412 
    413  SocketContext sock{fd, handler, 0};
    414 
    415  AddToIdleList(&sock);
    416  return NS_OK;
    417 }
    418 
    419 // the number of sockets that can be attached at any given time is
    420 // limited.  this is done because some operating systems (e.g., Win9x)
    421 // limit the number of sockets that can be created by an application.
    422 // AttachSocket will fail if the limit is exceeded.  consumers should
    423 // call CanAttachSocket and check the result before creating a socket.
    424 
    425 bool nsSocketTransportService::CanAttachSocket() {
    426  MOZ_ASSERT(!mShuttingDown);
    427  uint32_t total = mActiveList.Length() + mIdleList.Length();
    428  bool rv = total < gMaxCount;
    429 
    430  if (!rv) {
    431    static bool reported_socket_limit_reached = false;
    432    if (!reported_socket_limit_reached) {
    433      mozilla::glean::networking::os_socket_limit_reached.Add(1);
    434      reported_socket_limit_reached = true;
    435    }
    436    SOCKET_LOG(
    437        ("nsSocketTransportService::CanAttachSocket failed -  total: %d, "
    438         "maxCount: %d\n",
    439         total, gMaxCount));
    440  }
    441 
    442  MOZ_ASSERT(mInitialized);
    443  return rv;
    444 }
    445 
    446 nsresult nsSocketTransportService::DetachSocket(SocketContextList& listHead,
    447                                                SocketContext* sock) {
    448  SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n",
    449              sock->mHandler.get()));
    450  MOZ_ASSERT((&listHead == &mActiveList) || (&listHead == &mIdleList),
    451             "DetachSocket invalid head");
    452 
    453  {
    454    // inform the handler that this socket is going away
    455    sock->mHandler->OnSocketDetached(sock->mFD);
    456  }
    457  mSentBytesCount += sock->mHandler->ByteCountSent();
    458  mReceivedBytesCount += sock->mHandler->ByteCountReceived();
    459 
    460  // cleanup
    461  sock->mFD = nullptr;
    462 
    463  if (&listHead == &mActiveList) {
    464    RemoveFromPollList(sock);
    465  } else {
    466    RemoveFromIdleList(sock);
    467  }
    468 
    469  // NOTE: sock is now an invalid pointer
    470 
    471  //
    472  // notify the first element on the pending socket queue...
    473  //
    474  nsCOMPtr<nsIRunnable> event;
    475  LinkedRunnableEvent* runnable = mPendingSocketQueue.getFirst();
    476  if (runnable) {
    477    event = runnable->TakeEvent();
    478    runnable->remove();
    479    delete runnable;
    480  }
    481  if (event) {
    482    // move event from pending queue to dispatch queue
    483    return Dispatch(event, NS_DISPATCH_NORMAL);
    484  }
    485  return NS_OK;
    486 }
    487 
    488 // Returns the index of a SocketContext within a list, or -1 if it's
    489 // not a pointer to a list element
    490 // NOTE: this could be supplied by nsTArray<>
    491 int64_t nsSocketTransportService::SockIndex(SocketContextList& aList,
    492                                            SocketContext* aSock) {
    493  ptrdiff_t index = -1;
    494  if (!aList.IsEmpty()) {
    495    index = aSock - &aList[0];
    496    if (index < 0 || (size_t)index + 1 > aList.Length()) {
    497      index = -1;
    498    }
    499  }
    500  return (int64_t)index;
    501 }
    502 
    503 void nsSocketTransportService::AddToPollList(SocketContext* sock) {
    504  MOZ_ASSERT(SockIndex(mActiveList, sock) == -1,
    505             "AddToPollList Socket Already Active");
    506 
    507  SOCKET_LOG(("nsSocketTransportService::AddToPollList %p [handler=%p]\n", sock,
    508              sock->mHandler.get()));
    509 
    510  sock->EnsureTimeout(PR_IntervalNow());
    511  PRPollDesc poll;
    512  poll.fd = sock->mFD;
    513  poll.in_flags = sock->mHandler->mPollFlags;
    514  poll.out_flags = 0;
    515  if (ChaosMode::isActive(ChaosFeature::NetworkScheduling)) {
    516    auto newSocketIndex = mActiveList.Length();
    517    newSocketIndex = ChaosMode::randomUint32LessThan(newSocketIndex + 1);
    518    mActiveList.InsertElementAt(newSocketIndex, *sock);
    519    // mPollList is offset by 1
    520    mPollList.InsertElementAt(newSocketIndex + 1, poll);
    521  } else {
    522    // Avoid refcount bump/decrease
    523    mActiveList.EmplaceBack(sock->mFD, sock->mHandler.forget(),
    524                            sock->mPollStartEpoch);
    525    mPollList.AppendElement(poll);
    526  }
    527 
    528  SOCKET_LOG(
    529      ("  active=%zu idle=%zu\n", mActiveList.Length(), mIdleList.Length()));
    530 }
    531 
    532 void nsSocketTransportService::RemoveFromPollList(SocketContext* sock) {
    533  SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList %p [handler=%p]\n",
    534              sock, sock->mHandler.get()));
    535 
    536  auto index = SockIndex(mActiveList, sock);
    537  MOZ_RELEASE_ASSERT(index != -1, "invalid index");
    538 
    539  SOCKET_LOG(("  index=%" PRId64 " mActiveList.Length()=%zu\n", index,
    540              mActiveList.Length()));
    541  mActiveList.UnorderedRemoveElementAt(index);
    542  // mPollList is offset by 1
    543  mPollList.UnorderedRemoveElementAt(index + 1);
    544 
    545  SOCKET_LOG(
    546      ("  active=%zu idle=%zu\n", mActiveList.Length(), mIdleList.Length()));
    547 }
    548 
    549 void nsSocketTransportService::AddToIdleList(SocketContext* sock) {
    550  MOZ_ASSERT(SockIndex(mIdleList, sock) == -1,
    551             "AddToIdleList Socket Already Idle");
    552 
    553  SOCKET_LOG(("nsSocketTransportService::AddToIdleList %p [handler=%p]\n", sock,
    554              sock->mHandler.get()));
    555 
    556  // Avoid refcount bump/decrease
    557  mIdleList.EmplaceBack(sock->mFD, sock->mHandler.forget(),
    558                        sock->mPollStartEpoch);
    559 
    560  SOCKET_LOG(
    561      ("  active=%zu idle=%zu\n", mActiveList.Length(), mIdleList.Length()));
    562 }
    563 
    564 void nsSocketTransportService::RemoveFromIdleList(SocketContext* sock) {
    565  SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n",
    566              sock->mHandler.get()));
    567  auto index = SockIndex(mIdleList, sock);
    568  MOZ_RELEASE_ASSERT(index != -1);
    569  mIdleList.UnorderedRemoveElementAt(index);
    570 
    571  SOCKET_LOG(
    572      ("  active=%zu idle=%zu\n", mActiveList.Length(), mIdleList.Length()));
    573 }
    574 
    575 void nsSocketTransportService::MoveToIdleList(SocketContext* sock) {
    576  SOCKET_LOG(("nsSocketTransportService::MoveToIdleList %p [handler=%p]\n",
    577              sock, sock->mHandler.get()));
    578  MOZ_ASSERT(SockIndex(mIdleList, sock) == -1);
    579  MOZ_ASSERT(SockIndex(mActiveList, sock) != -1);
    580  AddToIdleList(sock);
    581  RemoveFromPollList(sock);
    582 }
    583 
    584 void nsSocketTransportService::MoveToPollList(SocketContext* sock) {
    585  SOCKET_LOG(("nsSocketTransportService::MoveToPollList %p [handler=%p]\n",
    586              sock, sock->mHandler.get()));
    587  MOZ_ASSERT(SockIndex(mIdleList, sock) != -1);
    588  MOZ_ASSERT(SockIndex(mActiveList, sock) == -1);
    589  AddToPollList(sock);
    590  RemoveFromIdleList(sock);
    591 }
    592 
    593 void nsSocketTransportService::ApplyPortRemapPreference(
    594    TPortRemapping const& portRemapping) {
    595  MOZ_ASSERT(IsOnCurrentThreadInfallible());
    596 
    597  mPortRemapping.reset();
    598  if (!portRemapping.IsEmpty()) {
    599    mPortRemapping.emplace(portRemapping);
    600  }
    601 }
    602 
    603 PRIntervalTime nsSocketTransportService::PollTimeout(PRIntervalTime now) {
    604  if (mActiveList.IsEmpty()) {
    605    return NS_SOCKET_POLL_TIMEOUT;
    606  }
    607 
    608  // compute minimum time before any socket timeout expires.
    609  PRIntervalTime minR = NS_SOCKET_POLL_TIMEOUT;
    610  for (uint32_t i = 0; i < mActiveList.Length(); ++i) {
    611    const SocketContext& s = mActiveList[i];
    612    PRIntervalTime r = s.TimeoutIn(now);
    613    if (r < minR) {
    614      minR = r;
    615    }
    616  }
    617  if (minR == NS_SOCKET_POLL_TIMEOUT) {
    618    SOCKET_LOG(("poll timeout: none\n"));
    619    return NS_SOCKET_POLL_TIMEOUT;
    620  }
    621  SOCKET_LOG(("poll timeout: %" PRIu32 "\n", PR_IntervalToSeconds(minR)));
    622  return minR;
    623 }
    624 
    625 int32_t nsSocketTransportService::Poll(PRIntervalTime ts) {
    626  MOZ_ASSERT(IsOnCurrentThread());
    627  PRPollDesc* firstPollEntry;
    628  uint32_t pollCount;
    629  PRIntervalTime pollTimeout;
    630 
    631  // If there are pending events for this thread then
    632  // DoPollIteration() should service the network without blocking.
    633  bool pendingEvents = false;
    634  mRawThread->HasPendingEvents(&pendingEvents);
    635 
    636  if (mPollList[0].fd) {
    637    mPollList[0].out_flags = 0;
    638    firstPollEntry = &mPollList[0];
    639    pollCount = mPollList.Length();
    640    pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout(ts);
    641  } else {
    642    // no pollable event, so busy wait...
    643    pollCount = mActiveList.Length();
    644    if (pollCount) {
    645      firstPollEntry = &mPollList[1];
    646    } else {
    647      firstPollEntry = nullptr;
    648    }
    649    pollTimeout =
    650        pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25);
    651  }
    652 
    653  if ((ts - mLastNetworkLinkChangeTime) < mNetworkLinkChangeBusyWaitPeriod) {
    654    // Being here means we are few seconds after a network change has
    655    // been detected.
    656    PRIntervalTime to = mNetworkLinkChangeBusyWaitTimeout;
    657    if (to) {
    658      pollTimeout = std::min(to, pollTimeout);
    659      SOCKET_LOG(("  timeout shorthened after network change event"));
    660    }
    661  }
    662 
    663  TimeStamp pollStart;
    664  if (Telemetry::CanRecordPrereleaseData()) {
    665    pollStart = TimeStamp::NowLoRes();
    666  }
    667 
    668  SOCKET_LOG(("    timeout = %i milliseconds\n",
    669              PR_IntervalToMilliseconds(pollTimeout)));
    670 
    671  int32_t n;
    672  {
    673 #ifdef MOZ_GECKO_PROFILER
    674    TimeStamp startTime = TimeStamp::Now();
    675    if (pollTimeout != PR_INTERVAL_NO_WAIT) {
    676      // There will be an actual non-zero wait, let the profiler know about it
    677      // by marking thread as sleeping around the polling call.
    678      profiler_thread_sleep();
    679    }
    680 #endif
    681 
    682    n = PR_Poll(firstPollEntry, pollCount, pollTimeout);
    683 
    684 #ifdef MOZ_GECKO_PROFILER
    685    if (pollTimeout != PR_INTERVAL_NO_WAIT) {
    686      profiler_thread_wake();
    687    }
    688    if (profiler_thread_is_being_profiled_for_markers()) {
    689      PROFILER_MARKER_TEXT(
    690          "SocketTransportService::Poll", NETWORK,
    691          MarkerTiming::IntervalUntilNowFrom(startTime),
    692          pollTimeout == PR_INTERVAL_NO_TIMEOUT
    693              ? nsPrintfCString("Poll count: %u, Poll timeout: NO_TIMEOUT",
    694                                pollCount)
    695          : pollTimeout == PR_INTERVAL_NO_WAIT
    696              ? nsPrintfCString("Poll count: %u, Poll timeout: NO_WAIT",
    697                                pollCount)
    698              : nsPrintfCString("Poll count: %u, Poll timeout: %ums", pollCount,
    699                                PR_IntervalToMilliseconds(pollTimeout)));
    700    }
    701 #endif
    702  }
    703 
    704  SOCKET_LOG(("    ...returned after %i milliseconds\n",
    705              PR_IntervalToMilliseconds(PR_IntervalNow() - ts)));
    706 
    707  return n;
    708 }
    709 
    710 //-----------------------------------------------------------------------------
    711 // xpcom api
    712 
    713 NS_IMPL_ISUPPORTS(nsSocketTransportService, nsISocketTransportService,
    714                  nsIRoutedSocketTransportService, nsIEventTarget,
    715                  nsISerialEventTarget, nsIThreadObserver, nsIRunnable,
    716                  nsPISocketTransportService, nsIObserver, nsINamed,
    717                  nsIDirectTaskDispatcher)
    718 
    719 static const char* gCallbackPrefs[] = {
    720    SEND_BUFFER_PREF,
    721    KEEPALIVE_ENABLED_PREF,
    722    KEEPALIVE_IDLE_TIME_PREF,
    723    KEEPALIVE_RETRY_INTERVAL_PREF,
    724    KEEPALIVE_PROBE_COUNT_PREF,
    725    MAX_TIME_BETWEEN_TWO_POLLS,
    726    MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN,
    727    POLLABLE_EVENT_TIMEOUT,
    728    "network.socket.forcePort",
    729    nullptr,
    730 };
    731 
    732 /* static */
    733 void nsSocketTransportService::UpdatePrefs(const char* aPref, void* aSelf) {
    734  static_cast<nsSocketTransportService*>(aSelf)->UpdatePrefs();
    735 }
    736 
    737 static uint32_t GetThreadStackSize() {
    738 #ifdef XP_WIN
    739  if (!StaticPrefs::network_allow_large_stack_size_for_socket_thread()) {
    740    return nsIThreadManager::DEFAULT_STACK_SIZE;
    741  }
    742 
    743  const uint32_t kWindowsThreadStackSize = 512 * 1024;
    744  // We can remove this custom stack size when DEFAULT_STACK_SIZE is increased.
    745  static_assert(kWindowsThreadStackSize > nsIThreadManager::DEFAULT_STACK_SIZE);
    746  return kWindowsThreadStackSize;
    747 #else
    748  return nsIThreadManager::DEFAULT_STACK_SIZE;
    749 #endif
    750 }
    751 
    752 // called from main thread only
    753 NS_IMETHODIMP
    754 nsSocketTransportService::Init() {
    755  if (!NS_IsMainThread()) {
    756    NS_ERROR("wrong thread");
    757    return NS_ERROR_UNEXPECTED;
    758  }
    759 
    760  if (mInitialized) {
    761    return NS_OK;
    762  }
    763 
    764  if (mShuttingDown) {
    765    return NS_ERROR_UNEXPECTED;
    766  }
    767 
    768  nsCOMPtr<nsIThread> thread;
    769 
    770  if (!XRE_IsContentProcess() ||
    771      StaticPrefs::network_allow_raw_sockets_in_content_processes_AtStartup()) {
    772    // Since we Poll, we can't use normal LongTask support in Main Process
    773    nsresult rv = NS_NewNamedThread(
    774        "Socket Thread", getter_AddRefs(thread), this,
    775        {GetThreadStackSize(), false, false, Some(SOCKET_THREAD_LONGTASK_MS)});
    776    NS_ENSURE_SUCCESS(rv, rv);
    777  } else {
    778    // In the child process, we just want a regular nsThread with no socket
    779    // polling. So we don't want to run the nsSocketTransportService runnable on
    780    // it.
    781    nsresult rv =
    782        NS_NewNamedThread("Socket Thread", getter_AddRefs(thread), nullptr,
    783                          {nsIThreadManager::DEFAULT_STACK_SIZE, false, false,
    784                           Some(SOCKET_THREAD_LONGTASK_MS)});
    785    NS_ENSURE_SUCCESS(rv, rv);
    786 
    787    // Set up some of the state that nsSocketTransportService::Run would set.
    788    PRThread* prthread = nullptr;
    789    thread->GetPRThread(&prthread);
    790    gSocketThread = prthread;
    791    mRawThread = thread;
    792  }
    793 
    794  {
    795    MutexAutoLock lock(mLock);
    796    // Install our mThread, protecting against concurrent readers
    797    thread.swap(mThread);
    798    mDirectTaskDispatcher = do_QueryInterface(mThread);
    799    MOZ_DIAGNOSTIC_ASSERT(
    800        mDirectTaskDispatcher,
    801        "Underlying thread must support direct task dispatching");
    802  }
    803 
    804  Preferences::RegisterCallbacks(UpdatePrefs, gCallbackPrefs, this);
    805  UpdatePrefs();
    806 
    807  nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
    808  // Note that the observr notifications are forwarded from parent process to
    809  // socket process. We have to make sure the topics registered below are also
    810  // registered in nsIOService::Init().
    811  if (obsSvc) {
    812    MOZ_ALWAYS_SUCCEEDS(
    813        obsSvc->AddObserver(this, "last-pb-context-exited", false));
    814    MOZ_ALWAYS_SUCCEEDS(
    815        obsSvc->AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC, false));
    816    MOZ_ALWAYS_SUCCEEDS(
    817        obsSvc->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, false));
    818    MOZ_ALWAYS_SUCCEEDS(
    819        obsSvc->AddObserver(this, "xpcom-shutdown-threads", false));
    820    MOZ_ALWAYS_SUCCEEDS(
    821        obsSvc->AddObserver(this, NS_NETWORK_LINK_TOPIC, false));
    822  }
    823 
    824  // We can now dispatch tasks to the socket thread.
    825  mInitialized = true;
    826  return NS_OK;
    827 }
    828 
    829 // called from main thread only
    830 NS_IMETHODIMP
    831 nsSocketTransportService::Shutdown(bool aXpcomShutdown) {
    832  SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
    833 
    834  NS_ENSURE_STATE(NS_IsMainThread());
    835 
    836  if (!mInitialized || mShuttingDown) {
    837    // We never inited, or shutdown has already started
    838    return NS_OK;
    839  }
    840 
    841  {
    842    auto observersCopy = mShutdownObservers;
    843    for (auto& observer : observersCopy) {
    844      observer->Observe();
    845    }
    846  }
    847 
    848  mShuttingDown = true;
    849 
    850  {
    851    MutexAutoLock lock(mLock);
    852 
    853    if (mPollableEvent) {
    854      mPollableEvent->Signal();
    855    }
    856  }
    857 
    858  // If we're shutting down due to going offline (rather than due to XPCOM
    859  // shutdown), also tear down the thread. The thread will be shutdown during
    860  // xpcom-shutdown-threads if during xpcom-shutdown proper.
    861  if (!aXpcomShutdown) {
    862    ShutdownThread();
    863  }
    864 
    865  return NS_OK;
    866 }
    867 
    868 nsresult nsSocketTransportService::ShutdownThread() {
    869  SOCKET_LOG(("nsSocketTransportService::ShutdownThread\n"));
    870 
    871  NS_ENSURE_STATE(NS_IsMainThread());
    872 
    873  if (!mInitialized) {
    874    return NS_OK;
    875  }
    876 
    877  // join with thread
    878  nsCOMPtr<nsIThread> thread = GetThreadSafely();
    879  thread->Shutdown();
    880  {
    881    MutexAutoLock lock(mLock);
    882    // Drop our reference to mThread and make sure that any concurrent readers
    883    // are excluded
    884    mThread = nullptr;
    885    mDirectTaskDispatcher = nullptr;
    886  }
    887 
    888  Preferences::UnregisterCallbacks(UpdatePrefs, gCallbackPrefs, this);
    889 
    890  nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
    891  if (obsSvc) {
    892    obsSvc->RemoveObserver(this, "last-pb-context-exited");
    893    obsSvc->RemoveObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC);
    894    obsSvc->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
    895    obsSvc->RemoveObserver(this, "xpcom-shutdown-threads");
    896    obsSvc->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
    897  }
    898 
    899  if (mAfterWakeUpTimer) {
    900    mAfterWakeUpTimer->Cancel();
    901    mAfterWakeUpTimer = nullptr;
    902  }
    903 
    904  mInitialized = false;
    905  mShuttingDown = false;
    906 
    907  return NS_OK;
    908 }
    909 
    910 NS_IMETHODIMP
    911 nsSocketTransportService::GetOffline(bool* offline) {
    912  MutexAutoLock lock(mLock);
    913  *offline = mOffline;
    914  return NS_OK;
    915 }
    916 
    917 NS_IMETHODIMP
    918 nsSocketTransportService::SetOffline(bool offline) {
    919  MutexAutoLock lock(mLock);
    920  if (!mOffline && offline) {
    921    // signal the socket thread to go offline, so it will detach sockets
    922    mGoingOffline = true;
    923    mOffline = true;
    924  } else if (mOffline && !offline) {
    925    mOffline = false;
    926  }
    927  if (mPollableEvent) {
    928    mPollableEvent->Signal();
    929  }
    930 
    931  return NS_OK;
    932 }
    933 
    934 NS_IMETHODIMP
    935 nsSocketTransportService::GetKeepaliveIdleTime(int32_t* aKeepaliveIdleTimeS) {
    936  MOZ_ASSERT(aKeepaliveIdleTimeS);
    937  if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
    938    return NS_ERROR_NULL_POINTER;
    939  }
    940  *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
    941  return NS_OK;
    942 }
    943 
    944 NS_IMETHODIMP
    945 nsSocketTransportService::GetKeepaliveRetryInterval(
    946    int32_t* aKeepaliveRetryIntervalS) {
    947  MOZ_ASSERT(aKeepaliveRetryIntervalS);
    948  if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
    949    return NS_ERROR_NULL_POINTER;
    950  }
    951  *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
    952  return NS_OK;
    953 }
    954 
    955 NS_IMETHODIMP
    956 nsSocketTransportService::GetKeepaliveProbeCount(
    957    int32_t* aKeepaliveProbeCount) {
    958  MOZ_ASSERT(aKeepaliveProbeCount);
    959  if (NS_WARN_IF(!aKeepaliveProbeCount)) {
    960    return NS_ERROR_NULL_POINTER;
    961  }
    962  *aKeepaliveProbeCount = mKeepaliveProbeCount;
    963  return NS_OK;
    964 }
    965 
    966 NS_IMETHODIMP
    967 nsSocketTransportService::CreateTransport(const nsTArray<nsCString>& types,
    968                                          const nsACString& host, int32_t port,
    969                                          nsIProxyInfo* proxyInfo,
    970                                          nsIDNSRecord* dnsRecord,
    971                                          nsISocketTransport** result) {
    972  return CreateRoutedTransport(types, host, port, ""_ns, 0, proxyInfo,
    973                               dnsRecord, result);
    974 }
    975 
    976 NS_IMETHODIMP
    977 nsSocketTransportService::CreateRoutedTransport(
    978    const nsTArray<nsCString>& types, const nsACString& host, int32_t port,
    979    const nsACString& hostRoute, int32_t portRoute, nsIProxyInfo* proxyInfo,
    980    nsIDNSRecord* dnsRecord, nsISocketTransport** result) {
    981  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
    982  NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
    983 
    984  RefPtr<nsSocketTransport> trans = new nsSocketTransport();
    985  nsresult rv = trans->Init(types, host, port, hostRoute, portRoute, proxyInfo,
    986                            dnsRecord);
    987  if (NS_FAILED(rv)) {
    988    return rv;
    989  }
    990 
    991  trans.forget(result);
    992  return NS_OK;
    993 }
    994 
    995 NS_IMETHODIMP
    996 nsSocketTransportService::CreateUnixDomainTransport(
    997    nsIFile* aPath, nsISocketTransport** result) {
    998 #ifdef XP_UNIX
    999  nsresult rv;
   1000 
   1001  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
   1002 
   1003  nsAutoCString path;
   1004  rv = aPath->GetNativePath(path);
   1005  NS_ENSURE_SUCCESS(rv, rv);
   1006 
   1007  RefPtr<nsSocketTransport> trans = new nsSocketTransport();
   1008 
   1009  rv = trans->InitWithFilename(path.get());
   1010  NS_ENSURE_SUCCESS(rv, rv);
   1011 
   1012  trans.forget(result);
   1013  return NS_OK;
   1014 #else
   1015  return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
   1016 #endif
   1017 }
   1018 
   1019 NS_IMETHODIMP
   1020 nsSocketTransportService::CreateUnixDomainAbstractAddressTransport(
   1021    const nsACString& aName, nsISocketTransport** result) {
   1022  // Abstract socket address is supported on Linux only
   1023 #ifdef XP_LINUX
   1024  RefPtr<nsSocketTransport> trans = new nsSocketTransport();
   1025  // First character of Abstract socket address is null
   1026  UniquePtr<char[]> name(new char[aName.Length() + 1]);
   1027  *(name.get()) = 0;
   1028  memcpy(name.get() + 1, aName.BeginReading(), aName.Length());
   1029  nsresult rv = trans->InitWithName(name.get(), aName.Length() + 1);
   1030  if (NS_FAILED(rv)) {
   1031    return rv;
   1032  }
   1033 
   1034  trans.forget(result);
   1035  return NS_OK;
   1036 #else
   1037  return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
   1038 #endif
   1039 }
   1040 
   1041 NS_IMETHODIMP
   1042 nsSocketTransportService::OnDispatchedEvent() {
   1043 #ifndef XP_WIN
   1044  // On windows poll can hang and this became worse when we introduced the
   1045  // patch for bug 698882 (see also bug 1292181), therefore we reverted the
   1046  // behavior on windows to be as before bug 698882, e.g. write to the socket
   1047  // also if an event dispatch is on the socket thread and writing to the
   1048  // socket for each event.
   1049  if (OnSocketThread()) {
   1050    // this check is redundant to one done inside ::Signal(), but
   1051    // we can do it here and skip obtaining the lock - given that
   1052    // this is a relatively common occurance its worth the
   1053    // redundant code
   1054    SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
   1055    return NS_OK;
   1056  }
   1057 #else
   1058  if (gIOService->IsNetTearingDown()) {
   1059    // Poll can hang sometimes. If we are in shutdown, we are going to
   1060    // start a watchdog. If we do not exit poll within
   1061    // REPAIR_POLLABLE_EVENT_TIME signal a pollable event again.
   1062    StartPollWatchdog();
   1063  }
   1064 #endif
   1065 
   1066  MutexAutoLock lock(mLock);
   1067  if (mPollableEvent) {
   1068    mPollableEvent->Signal();
   1069  }
   1070  return NS_OK;
   1071 }
   1072 
   1073 NS_IMETHODIMP
   1074 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal* thread,
   1075                                             bool mayWait) {
   1076  return NS_OK;
   1077 }
   1078 
   1079 NS_IMETHODIMP
   1080 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
   1081                                                bool eventWasProcessed) {
   1082  return NS_OK;
   1083 }
   1084 
   1085 void nsSocketTransportService::MarkTheLastElementOfPendingQueue() {
   1086  mServingPendingQueue = false;
   1087 }
   1088 
   1089 NS_IMETHODIMP
   1090 nsSocketTransportService::Run() {
   1091  SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount));
   1092 
   1093 #if defined(XP_WIN)
   1094  // see bug 1361495, gethostname() triggers winsock initialization.
   1095  // so do it here (on parent and child) to protect against it being done first
   1096  // accidentally on the main thread.. especially via PR_GetSystemInfo(). This
   1097  // will also improve latency of first real winsock operation
   1098  // ..
   1099  // If STS-thread is no longer needed this should still be run before exiting
   1100 
   1101  char ignoredStackBuffer[255];
   1102  (void)gethostname(ignoredStackBuffer, 255);
   1103 #endif
   1104 
   1105  psm::InitializeSSLServerCertVerificationThreads();
   1106 
   1107  gSocketThread = PR_GetCurrentThread();
   1108 
   1109  {
   1110    // See bug 1843384:
   1111    // Avoid blocking the main thread by allocating the PollableEvent outside
   1112    // the mutex. Still has the potential to hang the socket thread, but the
   1113    // main thread remains responsive.
   1114    PollableEvent* pollable = new PollableEvent();
   1115    MutexAutoLock lock(mLock);
   1116    mPollableEvent.reset(pollable);
   1117 
   1118    //
   1119    // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
   1120    // or similar software.
   1121    //
   1122    // NOTE: per bug 191739, this failure could also be caused by lack
   1123    // of a loopback device on Windows and OS/2 platforms (it creates
   1124    // a loopback socket pair on these platforms to implement a pollable
   1125    // event object).  if we can't create a pollable event, then we'll
   1126    // have to "busy wait" to implement the socket event queue :-(
   1127    //
   1128    if (!mPollableEvent->Valid()) {
   1129      mPollableEvent = nullptr;
   1130      NS_WARNING("running socket transport thread without a pollable event");
   1131      SOCKET_LOG(("running socket transport thread without a pollable event"));
   1132    }
   1133 
   1134    PRPollDesc entry = {mPollableEvent ? mPollableEvent->PollableFD() : nullptr,
   1135                        PR_POLL_READ | PR_POLL_EXCEPT, 0};
   1136    SOCKET_LOG(("Setting entry 0"));
   1137    mPollList[0] = entry;
   1138  }
   1139 
   1140  mRawThread = NS_GetCurrentThread();
   1141 
   1142  // Ensure a call to GetCurrentSerialEventTarget() returns this event target.
   1143  SerialEventTargetGuard guard(this);
   1144 
   1145  // hook ourselves up to observe event processing for this thread
   1146  nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(mRawThread);
   1147  threadInt->SetObserver(this);
   1148 
   1149  // make sure the pseudo random number generator is seeded on this thread
   1150  srand(static_cast<unsigned>(PR_Now()));
   1151 
   1152  // For the calculation of the duration of the last cycle (i.e. the last
   1153  // for-loop iteration before shutdown).
   1154  TimeStamp startOfCycleForLastCycleCalc;
   1155 
   1156  // For measuring of the poll iteration duration without time spent blocked
   1157  // in poll().
   1158  TimeStamp pollCycleStart;
   1159 
   1160  // For calculating the time needed for a new element to run.
   1161  TimeStamp startOfIteration;
   1162  TimeStamp startOfNextIteration;
   1163 
   1164  for (;;) {
   1165    bool pendingEvents = false;
   1166    if (Telemetry::CanRecordPrereleaseData()) {
   1167      startOfCycleForLastCycleCalc = TimeStamp::NowLoRes();
   1168      startOfNextIteration = TimeStamp::NowLoRes();
   1169    }
   1170    // We pop out to this loop when there are no pending events.
   1171    // If we don't reset these, we may not re-enter ProcessNextEvent()
   1172    // until we have events to process, and it may seem like we have
   1173    // an event running for a very long time.
   1174    mRawThread->SetRunningEventDelay(TimeDuration(), TimeStamp());
   1175 
   1176    do {
   1177      if (Telemetry::CanRecordPrereleaseData()) {
   1178        pollCycleStart = TimeStamp::NowLoRes();
   1179      }
   1180 
   1181      DoPollIteration();
   1182 
   1183      mRawThread->HasPendingEvents(&pendingEvents);
   1184      if (pendingEvents) {
   1185        if (!mServingPendingQueue) {
   1186          nsresult rv = Dispatch(
   1187              NewRunnableMethod(
   1188                  "net::nsSocketTransportService::"
   1189                  "MarkTheLastElementOfPendingQueue",
   1190                  this,
   1191                  &nsSocketTransportService::MarkTheLastElementOfPendingQueue),
   1192              nsIEventTarget::DISPATCH_NORMAL);
   1193          if (NS_FAILED(rv)) {
   1194            NS_WARNING(
   1195                "Could not dispatch a new event on the "
   1196                "socket thread.");
   1197          } else {
   1198            mServingPendingQueue = true;
   1199          }
   1200 
   1201          if (Telemetry::CanRecordPrereleaseData()) {
   1202            startOfIteration = startOfNextIteration;
   1203            // Everything that comes after this point will
   1204            // be served in the next iteration. If no even
   1205            // arrives, startOfNextIteration will be reset at the
   1206            // beginning of each for-loop.
   1207            startOfNextIteration = TimeStamp::NowLoRes();
   1208          }
   1209        }
   1210        TimeStamp eventQueueStart = TimeStamp::NowLoRes();
   1211        do {
   1212          NS_ProcessNextEvent(mRawThread);
   1213          pendingEvents = false;
   1214          mRawThread->HasPendingEvents(&pendingEvents);
   1215        } while (pendingEvents && mServingPendingQueue &&
   1216                 ((TimeStamp::NowLoRes() - eventQueueStart).ToMilliseconds() <
   1217                  mMaxTimePerPollIter));
   1218      }
   1219    } while (pendingEvents);
   1220 
   1221    bool goingOffline = false;
   1222    // now that our event queue is empty, check to see if we should exit
   1223    if (mShuttingDown) {
   1224      break;
   1225    }
   1226    {
   1227      MutexAutoLock lock(mLock);
   1228      if (mGoingOffline) {
   1229        mGoingOffline = false;
   1230        goingOffline = true;
   1231      }
   1232    }
   1233    // Avoid potential deadlock
   1234    if (goingOffline) {
   1235      Reset(true);
   1236    }
   1237  }
   1238 
   1239  SOCKET_LOG(("STS shutting down thread\n"));
   1240 
   1241  // detach all sockets, including locals
   1242  Reset(false);
   1243 
   1244  // We don't clear gSocketThread so that OnSocketThread() won't be a false
   1245  // alarm for events generated by stopping the SSL threads during shutdown.
   1246  psm::StopSSLServerCertVerificationThreads();
   1247 
   1248  // Final pass over the event queue. This makes sure that events posted by
   1249  // socket detach handlers get processed.
   1250  NS_ProcessPendingEvents(mRawThread);
   1251 
   1252  SOCKET_LOG(("STS thread exit\n"));
   1253  MOZ_ASSERT(mPollList.Length() == 1);
   1254  MOZ_ASSERT(mActiveList.IsEmpty());
   1255  MOZ_ASSERT(mIdleList.IsEmpty());
   1256 
   1257  return NS_OK;
   1258 }
   1259 
   1260 void nsSocketTransportService::DetachSocketWithGuard(
   1261    bool aGuardLocals, SocketContextList& socketList, int32_t index) {
   1262  bool isGuarded = false;
   1263  if (aGuardLocals) {
   1264    socketList[index].mHandler->IsLocal(&isGuarded);
   1265    if (!isGuarded) {
   1266      socketList[index].mHandler->KeepWhenOffline(&isGuarded);
   1267    }
   1268  }
   1269  if (!isGuarded) {
   1270    DetachSocket(socketList, &socketList[index]);
   1271  }
   1272 }
   1273 
   1274 void nsSocketTransportService::Reset(bool aGuardLocals) {
   1275  // detach any sockets
   1276  int32_t i;
   1277  for (i = mActiveList.Length() - 1; i >= 0; --i) {
   1278    DetachSocketWithGuard(aGuardLocals, mActiveList, i);
   1279  }
   1280  for (i = mIdleList.Length() - 1; i >= 0; --i) {
   1281    DetachSocketWithGuard(aGuardLocals, mIdleList, i);
   1282  }
   1283 }
   1284 
   1285 nsresult nsSocketTransportService::DoPollIteration() {
   1286  SOCKET_LOG(("STS poll iter\n"));
   1287 
   1288  PRIntervalTime now = PR_IntervalNow();
   1289 
   1290  // We can't have more than int32_max sockets in use
   1291  int32_t i, count;
   1292  //
   1293  // poll loop
   1294  //
   1295  // walk active list backwards to see if any sockets should actually be
   1296  // idle, then walk the idle list backwards to see if any idle sockets
   1297  // should become active.  take care to check only idle sockets that
   1298  // were idle to begin with ;-)
   1299  //
   1300  count = mIdleList.Length();
   1301  for (i = mActiveList.Length() - 1; i >= 0; --i) {
   1302    //---
   1303    SOCKET_LOG(("  active [%u] { handler=%p condition=%" PRIx32
   1304                " pollflags=%hu }\n",
   1305                i, mActiveList[i].mHandler.get(),
   1306                static_cast<uint32_t>(mActiveList[i].mHandler->mCondition),
   1307                mActiveList[i].mHandler->mPollFlags));
   1308    //---
   1309    if (NS_FAILED(mActiveList[i].mHandler->mCondition)) {
   1310      DetachSocket(mActiveList, &mActiveList[i]);
   1311    } else {
   1312      uint16_t in_flags = mActiveList[i].mHandler->mPollFlags;
   1313      if (in_flags == 0) {
   1314        MoveToIdleList(&mActiveList[i]);
   1315      } else {
   1316        // update poll flags
   1317        mPollList[i + 1].in_flags = in_flags;
   1318        mPollList[i + 1].out_flags = 0;
   1319        mActiveList[i].EnsureTimeout(now);
   1320      }
   1321    }
   1322  }
   1323  for (i = count - 1; i >= 0; --i) {
   1324    //---
   1325    SOCKET_LOG(("  idle [%u] { handler=%p condition=%" PRIx32
   1326                " pollflags=%hu }\n",
   1327                i, mIdleList[i].mHandler.get(),
   1328                static_cast<uint32_t>(mIdleList[i].mHandler->mCondition),
   1329                mIdleList[i].mHandler->mPollFlags));
   1330    //---
   1331    if (NS_FAILED(mIdleList[i].mHandler->mCondition)) {
   1332      DetachSocket(mIdleList, &mIdleList[i]);
   1333    } else if (mIdleList[i].mHandler->mPollFlags != 0) {
   1334      MoveToPollList(&mIdleList[i]);
   1335    }
   1336  }
   1337 
   1338  {
   1339    MutexAutoLock lock(mLock);
   1340    if (mPollableEvent) {
   1341      // we want to make sure the timeout is measured from the time
   1342      // we enter poll().  This method resets the timestamp to 'now',
   1343      // if we were first signalled between leaving poll() and here.
   1344      // If we didn't do this and processing events took longer than
   1345      // the allowed signal timeout, we would detect it as a
   1346      // false-positive.  AdjustFirstSignalTimestamp is then a no-op
   1347      // until mPollableEvent->Clear() is called.
   1348      mPollableEvent->AdjustFirstSignalTimestamp();
   1349    }
   1350  }
   1351 
   1352  SOCKET_LOG(("  calling PR_Poll [active=%zu idle=%zu]\n", mActiveList.Length(),
   1353              mIdleList.Length()));
   1354 
   1355  // Measures seconds spent while blocked on PR_Poll
   1356  int32_t n = 0;
   1357 
   1358  if (!gIOService->IsNetTearingDown()) {
   1359    // Let's not do polling during shutdown.
   1360 #if defined(XP_WIN)
   1361    StartPolling();
   1362 #endif
   1363    n = Poll(now);
   1364 #if defined(XP_WIN)
   1365    EndPolling();
   1366 #endif
   1367  }
   1368 
   1369  now = PR_IntervalNow();
   1370 #ifdef MOZ_GECKO_PROFILER
   1371  TimeStamp startTime;
   1372  bool profiling = profiler_thread_is_being_profiled_for_markers();
   1373  if (profiling) {
   1374    startTime = TimeStamp::Now();
   1375  }
   1376 #endif
   1377 
   1378  if (n < 0) {
   1379    SOCKET_LOG(("  PR_Poll error [%d] os error [%d]\n", PR_GetError(),
   1380                PR_GetOSError()));
   1381  } else {
   1382    //
   1383    // service "active" sockets...
   1384    //
   1385    for (i = 0; i < int32_t(mActiveList.Length()); ++i) {
   1386      PRPollDesc& desc = mPollList[i + 1];
   1387      SocketContext& s = mActiveList[i];
   1388      if (n > 0 && desc.out_flags != 0) {
   1389        s.DisengageTimeout();
   1390        s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
   1391      } else if (s.IsTimedOut(now)) {
   1392        SOCKET_LOG(("socket %p timed out", s.mHandler.get()));
   1393        s.DisengageTimeout();
   1394        s.mHandler->OnSocketReady(desc.fd, -1);
   1395      } else {
   1396        s.MaybeResetEpoch();
   1397      }
   1398    }
   1399    //
   1400    // check for "dead" sockets and remove them (need to do this in
   1401    // reverse order obviously).
   1402    //
   1403    for (i = mActiveList.Length() - 1; i >= 0; --i) {
   1404      if (NS_FAILED(mActiveList[i].mHandler->mCondition)) {
   1405        DetachSocket(mActiveList, &mActiveList[i]);
   1406      }
   1407    }
   1408 
   1409    {
   1410      MutexAutoLock lock(mLock);
   1411      // acknowledge pollable event (should not block)
   1412      if (n != 0 &&
   1413          (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT)) &&
   1414          mPollableEvent &&
   1415          ((mPollList[0].out_flags & PR_POLL_EXCEPT) ||
   1416           !mPollableEvent->Clear())) {
   1417        // On Windows, the TCP loopback connection in the
   1418        // pollable event may become broken when a laptop
   1419        // switches between wired and wireless networks or
   1420        // wakes up from hibernation.  We try to create a
   1421        // new pollable event.  If that fails, we fall back
   1422        // on "busy wait".
   1423        TryRepairPollableEvent();
   1424      }
   1425 
   1426      if (mPollableEvent &&
   1427          !mPollableEvent->IsSignallingAlive(mPollableEventTimeout)) {
   1428        SOCKET_LOG(("Pollable event signalling failed/timed out"));
   1429        TryRepairPollableEvent();
   1430      }
   1431    }
   1432  }
   1433 #ifdef MOZ_GECKO_PROFILER
   1434  if (profiling) {
   1435    TimeStamp endTime = TimeStamp::Now();
   1436    if ((endTime - startTime).ToMilliseconds() >= SOCKET_THREAD_LONGTASK_MS) {
   1437      struct LongTaskMarker {
   1438        static constexpr Span<const char> MarkerTypeName() {
   1439          return MakeStringSpan("SocketThreadLongTask");
   1440        }
   1441        static void StreamJSONMarkerData(
   1442            baseprofiler::SpliceableJSONWriter& aWriter) {
   1443          aWriter.StringProperty("category", "LongTask");
   1444        }
   1445        static MarkerSchema MarkerTypeDisplay() {
   1446          using MS = MarkerSchema;
   1447          MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
   1448          schema.AddKeyLabelFormat("category", "Type", MS::Format::String,
   1449                                   MS::PayloadFlags::Searchable);
   1450          return schema;
   1451        }
   1452      };
   1453 
   1454      profiler_add_marker(ProfilerString8View("LongTaskSocketProcessing"),
   1455                          geckoprofiler::category::OTHER,
   1456                          MarkerTiming::Interval(startTime, endTime),
   1457                          LongTaskMarker{});
   1458    }
   1459  }
   1460 
   1461 #endif
   1462 
   1463  return NS_OK;
   1464 }
   1465 
   1466 void nsSocketTransportService::UpdateSendBufferPref() {
   1467  int32_t bufferSize;
   1468 
   1469  // If the pref is set, honor it. 0 means use OS defaults.
   1470  nsresult rv = Preferences::GetInt(SEND_BUFFER_PREF, &bufferSize);
   1471  if (NS_SUCCEEDED(rv)) {
   1472    mSendBufferSize = bufferSize;
   1473    return;
   1474  }
   1475 
   1476 #if defined(XP_WIN)
   1477  mSendBufferSize = 131072 * 4;
   1478 #endif
   1479 }
   1480 
   1481 nsresult nsSocketTransportService::UpdatePrefs() {
   1482  mSendBufferSize = 0;
   1483 
   1484  UpdateSendBufferPref();
   1485 
   1486  // Default TCP Keepalive Values.
   1487  int32_t keepaliveIdleTimeS;
   1488  nsresult rv =
   1489      Preferences::GetInt(KEEPALIVE_IDLE_TIME_PREF, &keepaliveIdleTimeS);
   1490  if (NS_SUCCEEDED(rv)) {
   1491    mKeepaliveIdleTimeS = std::clamp(keepaliveIdleTimeS, 1, kMaxTCPKeepIdle);
   1492  }
   1493 
   1494  int32_t keepaliveRetryIntervalS;
   1495  rv = Preferences::GetInt(KEEPALIVE_RETRY_INTERVAL_PREF,
   1496                           &keepaliveRetryIntervalS);
   1497  if (NS_SUCCEEDED(rv)) {
   1498    mKeepaliveRetryIntervalS =
   1499        std::clamp(keepaliveRetryIntervalS, 1, kMaxTCPKeepIntvl);
   1500  }
   1501 
   1502  int32_t keepaliveProbeCount;
   1503  rv = Preferences::GetInt(KEEPALIVE_PROBE_COUNT_PREF, &keepaliveProbeCount);
   1504  if (NS_SUCCEEDED(rv)) {
   1505    mKeepaliveProbeCount = std::clamp(keepaliveProbeCount, 1, kMaxTCPKeepCount);
   1506  }
   1507  bool keepaliveEnabled = false;
   1508  rv = Preferences::GetBool(KEEPALIVE_ENABLED_PREF, &keepaliveEnabled);
   1509  if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
   1510    mKeepaliveEnabledPref = keepaliveEnabled;
   1511    OnKeepaliveEnabledPrefChange();
   1512  }
   1513 
   1514  int32_t maxTimePref;
   1515  rv = Preferences::GetInt(MAX_TIME_BETWEEN_TWO_POLLS, &maxTimePref);
   1516  if (NS_SUCCEEDED(rv) && maxTimePref >= 0) {
   1517    mMaxTimePerPollIter = maxTimePref;
   1518  }
   1519 
   1520  int32_t pollBusyWaitPeriod;
   1521  rv = Preferences::GetInt(POLL_BUSY_WAIT_PERIOD, &pollBusyWaitPeriod);
   1522  if (NS_SUCCEEDED(rv) && pollBusyWaitPeriod > 0) {
   1523    mNetworkLinkChangeBusyWaitPeriod = PR_SecondsToInterval(pollBusyWaitPeriod);
   1524  }
   1525 
   1526  int32_t pollBusyWaitPeriodTimeout;
   1527  rv = Preferences::GetInt(POLL_BUSY_WAIT_PERIOD_TIMEOUT,
   1528                           &pollBusyWaitPeriodTimeout);
   1529  if (NS_SUCCEEDED(rv) && pollBusyWaitPeriodTimeout > 0) {
   1530    mNetworkLinkChangeBusyWaitTimeout =
   1531        PR_SecondsToInterval(pollBusyWaitPeriodTimeout);
   1532  }
   1533 
   1534  int32_t maxTimeForPrClosePref;
   1535  rv = Preferences::GetInt(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN,
   1536                           &maxTimeForPrClosePref);
   1537  if (NS_SUCCEEDED(rv) && maxTimeForPrClosePref >= 0) {
   1538    mMaxTimeForPrClosePref = PR_MillisecondsToInterval(maxTimeForPrClosePref);
   1539  }
   1540 
   1541  int32_t pollableEventTimeout;
   1542  rv = Preferences::GetInt(POLLABLE_EVENT_TIMEOUT, &pollableEventTimeout);
   1543  if (NS_SUCCEEDED(rv) && pollableEventTimeout >= 0) {
   1544    MutexAutoLock lock(mLock);
   1545    mPollableEventTimeout = TimeDuration::FromSeconds(pollableEventTimeout);
   1546  }
   1547 
   1548  nsAutoCString portMappingPref;
   1549  rv = Preferences::GetCString("network.socket.forcePort", portMappingPref);
   1550  if (NS_SUCCEEDED(rv)) {
   1551    bool rv = UpdatePortRemapPreference(portMappingPref);
   1552    if (!rv) {
   1553      NS_ERROR(
   1554          "network.socket.forcePort preference is ill-formed, this will likely "
   1555          "make everything unexpectedly fail!");
   1556    }
   1557  }
   1558 
   1559  return NS_OK;
   1560 }
   1561 
   1562 void nsSocketTransportService::OnKeepaliveEnabledPrefChange() {
   1563  // Dispatch to socket thread if we're not executing there.
   1564  if (!OnSocketThread()) {
   1565    gSocketTransportService->Dispatch(
   1566        NewRunnableMethod(
   1567            "net::nsSocketTransportService::OnKeepaliveEnabledPrefChange", this,
   1568            &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
   1569        NS_DISPATCH_NORMAL);
   1570    return;
   1571  }
   1572 
   1573  SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
   1574              mKeepaliveEnabledPref ? "enabled" : "disabled"));
   1575 
   1576  // Notify each socket that keepalive has been en/disabled globally.
   1577  for (int32_t i = mActiveList.Length() - 1; i >= 0; --i) {
   1578    NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
   1579  }
   1580  for (int32_t i = mIdleList.Length() - 1; i >= 0; --i) {
   1581    NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
   1582  }
   1583 }
   1584 
   1585 void nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(
   1586    SocketContext* sock) {
   1587  MOZ_ASSERT(sock, "SocketContext cannot be null!");
   1588  MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
   1589 
   1590  if (!sock || !sock->mHandler) {
   1591    return;
   1592  }
   1593 
   1594  sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
   1595 }
   1596 
   1597 NS_IMETHODIMP
   1598 nsSocketTransportService::GetName(nsACString& aName) {
   1599  aName.AssignLiteral("nsSocketTransportService");
   1600  return NS_OK;
   1601 }
   1602 
   1603 NS_IMETHODIMP
   1604 nsSocketTransportService::Observe(nsISupports* subject, const char* topic,
   1605                                  const char16_t* data) {
   1606  SOCKET_LOG(("nsSocketTransportService::Observe topic=%s", topic));
   1607 
   1608  if (!strcmp(topic, "last-pb-context-exited")) {
   1609    nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(
   1610        "net::nsSocketTransportService::ClosePrivateConnections", this,
   1611        &nsSocketTransportService::ClosePrivateConnections);
   1612    nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
   1613    NS_ENSURE_SUCCESS(rv, rv);
   1614  }
   1615 
   1616  if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
   1617    nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
   1618    if (timer == mAfterWakeUpTimer) {
   1619      mAfterWakeUpTimer = nullptr;
   1620      mSleepPhase = false;
   1621    }
   1622 
   1623 #if defined(XP_WIN)
   1624    if (timer == mPollRepairTimer) {
   1625      DoPollRepair();
   1626    }
   1627 #endif
   1628 
   1629  } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
   1630    mSleepPhase = true;
   1631    if (mAfterWakeUpTimer) {
   1632      mAfterWakeUpTimer->Cancel();
   1633      mAfterWakeUpTimer = nullptr;
   1634    }
   1635  } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
   1636    if (mSleepPhase && !mAfterWakeUpTimer) {
   1637      NS_NewTimerWithObserver(getter_AddRefs(mAfterWakeUpTimer), this, 2000,
   1638                              nsITimer::TYPE_ONE_SHOT);
   1639    }
   1640  } else if (!strcmp(topic, "xpcom-shutdown-threads")) {
   1641    ShutdownThread();
   1642  } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
   1643    mLastNetworkLinkChangeTime = PR_IntervalNow();
   1644  }
   1645 
   1646  return NS_OK;
   1647 }
   1648 
   1649 void nsSocketTransportService::ClosePrivateConnections() {
   1650  MOZ_ASSERT(IsOnCurrentThread(), "Must be called on the socket thread");
   1651 
   1652  for (int32_t i = mActiveList.Length() - 1; i >= 0; --i) {
   1653    if (mActiveList[i].mHandler->mIsPrivate) {
   1654      DetachSocket(mActiveList, &mActiveList[i]);
   1655    }
   1656  }
   1657  for (int32_t i = mIdleList.Length() - 1; i >= 0; --i) {
   1658    if (mIdleList[i].mHandler->mIsPrivate) {
   1659      DetachSocket(mIdleList, &mIdleList[i]);
   1660    }
   1661  }
   1662 }
   1663 
   1664 NS_IMETHODIMP
   1665 nsSocketTransportService::GetSendBufferSize(int32_t* value) {
   1666  *value = mSendBufferSize;
   1667  return NS_OK;
   1668 }
   1669 
   1670 /// ugly OS specific includes are placed at the bottom of the src for clarity
   1671 
   1672 #if defined(XP_WIN)
   1673 #  include <windows.h>
   1674 #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
   1675 #  include <sys/resource.h>
   1676 #endif
   1677 
   1678 PRStatus nsSocketTransportService::DiscoverMaxCount() {
   1679  gMaxCount = SOCKET_LIMIT_MIN;
   1680 
   1681 #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
   1682  // On unix and os x network sockets and file
   1683  // descriptors are the same. OS X comes defaulted at 256,
   1684  // most linux at 1000. We can reliably use [sg]rlimit to
   1685  // query that and raise it if needed.
   1686 
   1687  struct rlimit rlimitData{};
   1688  if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) {  // rlimit broken - use min
   1689    return PR_SUCCESS;
   1690  }
   1691 
   1692  if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET) {  // larger than target!
   1693    gMaxCount = SOCKET_LIMIT_TARGET;
   1694    return PR_SUCCESS;
   1695  }
   1696 
   1697  int32_t maxallowed = rlimitData.rlim_max;
   1698  if ((uint32_t)maxallowed <= SOCKET_LIMIT_MIN) {
   1699    return PR_SUCCESS;  // so small treat as if rlimit is broken
   1700  }
   1701 
   1702  if ((maxallowed == -1) ||  // no hard cap - ok to set target
   1703      ((uint32_t)maxallowed >= SOCKET_LIMIT_TARGET)) {
   1704    maxallowed = SOCKET_LIMIT_TARGET;
   1705  }
   1706 
   1707  rlimitData.rlim_cur = maxallowed;
   1708  setrlimit(RLIMIT_NOFILE, &rlimitData);
   1709  if ((getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) &&
   1710      (rlimitData.rlim_cur > SOCKET_LIMIT_MIN)) {
   1711    gMaxCount = rlimitData.rlim_cur;
   1712  }
   1713 
   1714 #elif defined(XP_WIN) && !defined(WIN_CE)
   1715  // >= XP is confirmed to have at least 1000
   1716  static_assert(SOCKET_LIMIT_TARGET <= 1000,
   1717                "SOCKET_LIMIT_TARGET max value is 1000");
   1718  gMaxCount = SOCKET_LIMIT_TARGET;
   1719 #else
   1720  // other platforms are harder to test - so leave at safe legacy value
   1721 #endif
   1722 
   1723  return PR_SUCCESS;
   1724 }
   1725 
   1726 // Used to return connection info to Dashboard.cpp
   1727 void nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo>* data,
   1728                                                 SocketContext* context,
   1729                                                 bool aActive) {
   1730  if (context->mHandler->mIsPrivate) {
   1731    return;
   1732  }
   1733  PRFileDesc* aFD = context->mFD;
   1734 
   1735  PRFileDesc* idLayer = PR_GetIdentitiesLayer(aFD, PR_NSPR_IO_LAYER);
   1736 
   1737  NS_ENSURE_TRUE_VOID(idLayer);
   1738 
   1739  PRDescType type = PR_GetDescType(idLayer);
   1740  char host[64] = {0};
   1741  uint16_t port;
   1742  const char* type_desc;
   1743 
   1744  if (type == PR_DESC_SOCKET_TCP) {
   1745    type_desc = "TCP";
   1746    PRNetAddr peer_addr;
   1747    PodZero(&peer_addr);
   1748 
   1749    PRStatus rv = PR_GetPeerName(aFD, &peer_addr);
   1750    if (rv != PR_SUCCESS) {
   1751      return;
   1752    }
   1753 
   1754    rv = PR_NetAddrToString(&peer_addr, host, sizeof(host));
   1755    if (rv != PR_SUCCESS) {
   1756      return;
   1757    }
   1758 
   1759    if (peer_addr.raw.family == PR_AF_INET) {
   1760      port = peer_addr.inet.port;
   1761    } else {
   1762      port = peer_addr.ipv6.port;
   1763    }
   1764    port = PR_ntohs(port);
   1765  } else {
   1766    if (type == PR_DESC_SOCKET_UDP) {
   1767      type_desc = "UDP";
   1768    } else {
   1769      type_desc = "other";
   1770    }
   1771    NetAddr addr;
   1772    if (context->mHandler->GetRemoteAddr(&addr) != NS_OK) {
   1773      return;
   1774    }
   1775    if (!addr.ToStringBuffer(host, sizeof(host))) {
   1776      return;
   1777    }
   1778    if (addr.GetPort(&port) != NS_OK) {
   1779      return;
   1780    }
   1781  }
   1782 
   1783  uint64_t sent = context->mHandler->ByteCountSent();
   1784  uint64_t received = context->mHandler->ByteCountReceived();
   1785  SocketInfo info = {nsCString(host),     sent, received, port, aActive,
   1786                     nsCString(type_desc)};
   1787 
   1788  data->AppendElement(info);
   1789 }
   1790 
   1791 void nsSocketTransportService::GetSocketConnections(
   1792    nsTArray<SocketInfo>* data) {
   1793  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1794  for (uint32_t i = 0; i < mActiveList.Length(); i++) {
   1795    AnalyzeConnection(data, &mActiveList[i], true);
   1796  }
   1797  for (uint32_t i = 0; i < mIdleList.Length(); i++) {
   1798    AnalyzeConnection(data, &mIdleList[i], false);
   1799  }
   1800 }
   1801 
   1802 bool nsSocketTransportService::IsTelemetryEnabledAndNotSleepPhase() {
   1803  return Telemetry::CanRecordPrereleaseData() && !mSleepPhase;
   1804 }
   1805 
   1806 #if defined(XP_WIN)
   1807 void nsSocketTransportService::StartPollWatchdog() {
   1808  // Start off the timer from a runnable off of the main thread in order to
   1809  // avoid a deadlock, see bug 1370448.
   1810  RefPtr<nsSocketTransportService> self(this);
   1811  NS_DispatchToMainThread(NS_NewRunnableFunction(
   1812      "nsSocketTransportService::StartPollWatchdog", [self] {
   1813        MutexAutoLock lock(self->mLock);
   1814 
   1815        // Poll can hang sometimes. If we are in shutdown, we are going to start
   1816        // a watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME
   1817        // signal a pollable event again.
   1818        if (gIOService->IsNetTearingDown() && self->mPolling &&
   1819            !self->mPollRepairTimer) {
   1820          NS_NewTimerWithObserver(getter_AddRefs(self->mPollRepairTimer), self,
   1821                                  REPAIR_POLLABLE_EVENT_TIME,
   1822                                  nsITimer::TYPE_REPEATING_SLACK);
   1823        }
   1824      }));
   1825 }
   1826 
   1827 void nsSocketTransportService::DoPollRepair() {
   1828  MutexAutoLock lock(mLock);
   1829  if (mPolling && mPollableEvent) {
   1830    mPollableEvent->Signal();
   1831  } else if (mPollRepairTimer) {
   1832    mPollRepairTimer->Cancel();
   1833  }
   1834 }
   1835 
   1836 void nsSocketTransportService::StartPolling() {
   1837  MutexAutoLock lock(mLock);
   1838  mPolling = true;
   1839 }
   1840 
   1841 void nsSocketTransportService::EndPolling() {
   1842  MutexAutoLock lock(mLock);
   1843  mPolling = false;
   1844  if (mPollRepairTimer) {
   1845    mPollRepairTimer->Cancel();
   1846  }
   1847 }
   1848 
   1849 #endif
   1850 
   1851 void nsSocketTransportService::TryRepairPollableEvent() MOZ_REQUIRES(mLock) {
   1852  mLock.AssertCurrentThreadOwns();
   1853 
   1854  PollableEvent* pollable = nullptr;
   1855  {
   1856    // Bug 1719046: In certain cases PollableEvent constructor can hang
   1857    // when callign PR_NewTCPSocketPair.
   1858    // We unlock the mutex to prevent main thread hangs acquiring the lock.
   1859    MutexAutoUnlock unlock(mLock);
   1860    pollable = new PollableEvent();
   1861  }
   1862 
   1863  NS_WARNING("Trying to repair mPollableEvent");
   1864  mPollableEvent.reset(pollable);
   1865  if (!mPollableEvent->Valid()) {
   1866    mPollableEvent = nullptr;
   1867  }
   1868  SOCKET_LOG(
   1869      ("running socket transport thread without "
   1870       "a pollable event now valid=%d",
   1871       !!mPollableEvent));
   1872  mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
   1873  mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
   1874  mPollList[0].out_flags = 0;
   1875 }
   1876 
   1877 NS_IMETHODIMP
   1878 nsSocketTransportService::AddShutdownObserver(
   1879    nsISTSShutdownObserver* aObserver) {
   1880  mShutdownObservers.AppendElement(aObserver);
   1881  return NS_OK;
   1882 }
   1883 
   1884 NS_IMETHODIMP
   1885 nsSocketTransportService::RemoveShutdownObserver(
   1886    nsISTSShutdownObserver* aObserver) {
   1887  mShutdownObservers.RemoveElement(aObserver);
   1888  return NS_OK;
   1889 }
   1890 
   1891 }  // namespace net
   1892 }  // namespace mozilla