tor-browser

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

PollableEvent.cpp (12676B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsSocketTransportService2.h"
      8 #include "PollableEvent.h"
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/DebugOnly.h"
     11 #include "mozilla/Logging.h"
     12 #include "mozilla/net/DNS.h"
     13 #include "prerror.h"
     14 #include "prio.h"
     15 #include "private/pprio.h"
     16 #include "prnetdb.h"
     17 
     18 #ifdef XP_WIN
     19 #  include "ShutdownLayer.h"
     20 #else
     21 #  include <fcntl.h>
     22 #  define USEPIPE 1
     23 #endif
     24 
     25 // PRFilePrivate is defined in a private header of NSPR (primpl.h) we can't
     26 // include. We need this to be able to set nonblocking=true which PR_CreatePipe
     27 // fails to do. We can remove this after bug 1993248 is fixed.
     28 // Note that nsLocalFileWin.cpp also redefines this in order to change the
     29 // appendMode.
     30 //-----------------------------------------------------------------------------
     31 typedef enum {
     32  _PR_TRI_TRUE = 1,
     33  _PR_TRI_FALSE = 0,
     34  _PR_TRI_UNKNOWN = -1
     35 } _PRTriStateBool;
     36 
     37 struct _MDFileDesc {
     38  PROsfd osfd;
     39 };
     40 
     41 struct PRFilePrivate {
     42  int32_t state;
     43  bool nonblocking;
     44  _PRTriStateBool inheritable;
     45  PRFileDesc* next;
     46  int lockCount; /*   0: not locked
     47                  *  -1: a native lockfile call is in progress
     48                  * > 0: # times the file is locked */
     49  bool appendMode;
     50  _MDFileDesc md;
     51 };
     52 
     53 namespace mozilla {
     54 namespace net {
     55 
     56 #ifndef USEPIPE
     57 static PRDescIdentity sPollableEventLayerIdentity;
     58 static PRIOMethods sPollableEventLayerMethods;
     59 static PRIOMethods* sPollableEventLayerMethodsPtr = nullptr;
     60 
     61 static void LazyInitSocket() {
     62  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     63  if (sPollableEventLayerMethodsPtr) {
     64    return;
     65  }
     66  sPollableEventLayerIdentity = PR_GetUniqueIdentity("PollableEvent Layer");
     67  sPollableEventLayerMethods = *PR_GetDefaultIOMethods();
     68  sPollableEventLayerMethodsPtr = &sPollableEventLayerMethods;
     69 }
     70 
     71 static bool NewTCPSocketPair(PRFileDesc* fd[], bool aSetRecvBuff) {
     72  // this is a replacement for PR_NewTCPSocketPair that manually
     73  // sets the recv buffer to 64K. A windows bug (1248358)
     74  // can result in using an incompatible rwin and window
     75  // scale option on localhost pipes if not set before connect.
     76 
     77  SOCKET_LOG(("NewTCPSocketPair %s a recv buffer tuning\n",
     78              aSetRecvBuff ? "with" : "without"));
     79 
     80  PRFileDesc* listener = nullptr;
     81  PRFileDesc* writer = nullptr;
     82  PRFileDesc* reader = nullptr;
     83  PRSocketOptionData recvBufferOpt;
     84  recvBufferOpt.option = PR_SockOpt_RecvBufferSize;
     85  recvBufferOpt.value.recv_buffer_size = 65535;
     86 
     87  PRSocketOptionData nodelayOpt;
     88  nodelayOpt.option = PR_SockOpt_NoDelay;
     89  nodelayOpt.value.no_delay = true;
     90 
     91  PRSocketOptionData noblockOpt;
     92  noblockOpt.option = PR_SockOpt_Nonblocking;
     93  noblockOpt.value.non_blocking = true;
     94 
     95  listener = PR_OpenTCPSocket(PR_AF_INET);
     96  if (!listener) {
     97    goto failed;
     98  }
     99 
    100  if (aSetRecvBuff) {
    101    PR_SetSocketOption(listener, &recvBufferOpt);
    102  }
    103  PR_SetSocketOption(listener, &nodelayOpt);
    104 
    105  PRNetAddr listenAddr;
    106  memset(&listenAddr, 0, sizeof(listenAddr));
    107  if ((PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &listenAddr) == PR_FAILURE) ||
    108      (PR_Bind(listener, &listenAddr) == PR_FAILURE) ||
    109      (PR_GetSockName(listener, &listenAddr) ==
    110       PR_FAILURE) ||  // learn the dynamic port
    111      (PR_Listen(listener, 5) == PR_FAILURE)) {
    112    goto failed;
    113  }
    114 
    115  writer = PR_OpenTCPSocket(PR_AF_INET);
    116  if (!writer) {
    117    goto failed;
    118  }
    119  if (aSetRecvBuff) {
    120    PR_SetSocketOption(writer, &recvBufferOpt);
    121  }
    122  PR_SetSocketOption(writer, &nodelayOpt);
    123  PR_SetSocketOption(writer, &noblockOpt);
    124  PRNetAddr writerAddr;
    125  if (PR_InitializeNetAddr(PR_IpAddrLoopback, ntohs(listenAddr.inet.port),
    126                           &writerAddr) == PR_FAILURE) {
    127    goto failed;
    128  }
    129 
    130  if (PR_Connect(writer, &writerAddr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
    131    if ((PR_GetError() != PR_IN_PROGRESS_ERROR) ||
    132        (PR_ConnectContinue(writer, PR_POLL_WRITE) == PR_FAILURE)) {
    133      goto failed;
    134    }
    135  }
    136  PR_SetFDInheritable(writer, false);
    137 
    138  reader = PR_Accept(listener, &listenAddr, PR_MillisecondsToInterval(200));
    139  if (!reader) {
    140    goto failed;
    141  }
    142  PR_SetFDInheritable(reader, false);
    143  if (aSetRecvBuff) {
    144    PR_SetSocketOption(reader, &recvBufferOpt);
    145  }
    146  PR_SetSocketOption(reader, &nodelayOpt);
    147  PR_SetSocketOption(reader, &noblockOpt);
    148  PR_Close(listener);
    149 
    150  fd[0] = reader;
    151  fd[1] = writer;
    152  return true;
    153 
    154 failed:
    155  if (listener) {
    156    PR_Close(listener);
    157  }
    158  if (reader) {
    159    PR_Close(reader);
    160  }
    161  if (writer) {
    162    PR_Close(writer);
    163  }
    164  return false;
    165 }
    166 
    167 #endif
    168 
    169 PollableEvent::PollableEvent()
    170 
    171 {
    172  MOZ_COUNT_CTOR(PollableEvent);
    173  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    174  // create pair of prfiledesc that can be used as a poll()ble
    175  // signal. on windows use a localhost socket pair, and on
    176  // unix use a pipe.
    177 #ifdef USEPIPE
    178  SOCKET_LOG(("PollableEvent() using pipe\n"));
    179  if (PR_CreatePipe(&mReadFD, &mWriteFD) == PR_SUCCESS) {
    180    // make the pipe non blocking. NSPR asserts at
    181    // trying to use SockOpt here
    182    PROsfd fd = PR_FileDesc2NativeHandle(mReadFD);
    183    int flags = fcntl(fd, F_GETFL, 0);
    184    (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    185    fd = PR_FileDesc2NativeHandle(mWriteFD);
    186    flags = fcntl(fd, F_GETFL, 0);
    187    (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    188 
    189    // Bug 1993248 - PR_CreatePipe doesn't set this flag, so we need to do it
    190    // manually to ensure read operations are not blocking. This hack can be
    191    // removed once this is fixed in NSPR.
    192    mReadFD->secret->nonblocking = true;
    193    mWriteFD->secret->nonblocking = true;
    194  } else {
    195    mReadFD = nullptr;
    196    mWriteFD = nullptr;
    197    SOCKET_LOG(("PollableEvent() pipe failed\n"));
    198  }
    199 #else
    200  SOCKET_LOG(("PollableEvent() using socket pair\n"));
    201  PRFileDesc* fd[2];
    202  LazyInitSocket();
    203 
    204  // Try with a increased recv buffer first (bug 1248358).
    205  if (NewTCPSocketPair(fd, true)) {
    206    mReadFD = fd[0];
    207    mWriteFD = fd[1];
    208    // If the previous fails try without recv buffer increase (bug 1305436).
    209  } else if (NewTCPSocketPair(fd, false)) {
    210    mReadFD = fd[0];
    211    mWriteFD = fd[1];
    212    // If both fail, try the old version.
    213  } else if (PR_NewTCPSocketPair(fd) == PR_SUCCESS) {
    214    mReadFD = fd[0];
    215    mWriteFD = fd[1];
    216 
    217    PRSocketOptionData socket_opt;
    218    DebugOnly<PRStatus> status;
    219    socket_opt.option = PR_SockOpt_NoDelay;
    220    socket_opt.value.no_delay = true;
    221    PR_SetSocketOption(mWriteFD, &socket_opt);
    222    PR_SetSocketOption(mReadFD, &socket_opt);
    223    socket_opt.option = PR_SockOpt_Nonblocking;
    224    socket_opt.value.non_blocking = true;
    225    status = PR_SetSocketOption(mWriteFD, &socket_opt);
    226    MOZ_ASSERT(status == PR_SUCCESS);
    227    status = PR_SetSocketOption(mReadFD, &socket_opt);
    228    MOZ_ASSERT(status == PR_SUCCESS);
    229  }
    230 
    231  if (mReadFD && mWriteFD) {
    232    // compatibility with LSPs such as McAfee that assume a NSPR
    233    // layer for read ala the nspr Pollable Event - Bug 698882. This layer is a
    234    // nop.
    235    PRFileDesc* topLayer = PR_CreateIOLayerStub(sPollableEventLayerIdentity,
    236                                                sPollableEventLayerMethodsPtr);
    237    if (topLayer) {
    238      if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, topLayer) == PR_FAILURE) {
    239        topLayer->dtor(topLayer);
    240      } else {
    241        SOCKET_LOG(("PollableEvent() nspr layer ok\n"));
    242        mReadFD = topLayer;
    243      }
    244    }
    245 
    246  } else {
    247    SOCKET_LOG(("PollableEvent() socketpair failed\n"));
    248  }
    249 #endif
    250 
    251  if (mReadFD && mWriteFD) {
    252    // prime the system to deal with races invovled in [dc]tor cycle
    253    SOCKET_LOG(("PollableEvent() ctor ok\n"));
    254    mSignaled = true;
    255    MarkFirstSignalTimestamp();
    256    PR_Write(mWriteFD, "I", 1);
    257  }
    258 }
    259 
    260 PollableEvent::~PollableEvent() {
    261  MOZ_COUNT_DTOR(PollableEvent);
    262  if (mWriteFD) {
    263 #if defined(XP_WIN)
    264    AttachShutdownLayer(mWriteFD);
    265 #endif
    266    PR_Close(mWriteFD);
    267  }
    268  if (mReadFD) {
    269 #if defined(XP_WIN)
    270    AttachShutdownLayer(mReadFD);
    271 #endif
    272    PR_Close(mReadFD);
    273  }
    274 }
    275 
    276 // we do not record signals on the socket thread
    277 // because the socket thread can reliably look at its
    278 // own runnable queue before selecting a poll time
    279 // this is the "service the network without blocking" comment in
    280 // nsSocketTransportService2.cpp
    281 bool PollableEvent::Signal() {
    282  SOCKET_LOG(("PollableEvent::Signal\n"));
    283 
    284  if (!mWriteFD) {
    285    SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n"));
    286    return false;
    287  }
    288 #ifndef XP_WIN
    289  // On windows poll can hang and this became worse when we introduced the
    290  // patch for bug 698882 (see also bug 1292181), therefore we reverted the
    291  // behavior on windows to be as before bug 698882, e.g. write to the socket
    292  // also if an event dispatch is on the socket thread and writing to the
    293  // socket for each event. See bug 1292181.
    294  if (OnSocketThread()) {
    295    SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n"));
    296    return true;
    297  }
    298 #endif
    299 
    300 #ifndef XP_WIN
    301  // To wake up the poll writing once is enough, but for Windows that can cause
    302  // hangs so we will write for every event.
    303  // For non-Windows systems it is enough to write just once.
    304  if (mSignaled) {
    305    return true;
    306  }
    307 #endif
    308 
    309  if (!mSignaled) {
    310    mSignaled = true;
    311    MarkFirstSignalTimestamp();
    312  }
    313 
    314  int32_t status = PR_Write(mWriteFD, "M", 1);
    315  SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status));
    316  if (status != 1) {
    317    NS_WARNING("PollableEvent::Signal Failed\n");
    318    SOCKET_LOG(("PollableEvent::Signal Failed\n"));
    319    mSignaled = false;
    320    mWriteFailed = true;
    321  } else {
    322    mWriteFailed = false;
    323  }
    324  return (status == 1);
    325 }
    326 
    327 bool PollableEvent::Clear() {
    328  // necessary because of the "dont signal on socket thread" optimization
    329  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    330 
    331  SOCKET_LOG(("PollableEvent::Clear\n"));
    332 
    333  if (!mFirstSignalAfterClear.IsNull()) {
    334    SOCKET_LOG(("PollableEvent::Clear time to signal %ums",
    335                (uint32_t)(TimeStamp::NowLoRes() - mFirstSignalAfterClear)
    336                    .ToMilliseconds()));
    337  }
    338 
    339  mFirstSignalAfterClear = TimeStamp();
    340  mSignalTimestampAdjusted = false;
    341  mSignaled = false;
    342 
    343  if (!mReadFD) {
    344    SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n"));
    345    return false;
    346  }
    347 
    348  char buf[2048];
    349  int32_t status;
    350 #ifdef XP_WIN
    351  // On Windows we are writing to the socket for each event, to be sure that we
    352  // do not have any deadlock read from the socket as much as we can.
    353  while (true) {
    354    status = PR_Read(mReadFD, buf, 2048);
    355    SOCKET_LOG(("PollableEvent::Clear PR_Read %d\n", status));
    356    if (status == 0) {
    357      SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
    358      return false;
    359    }
    360    if (status < 0) {
    361      PRErrorCode code = PR_GetError();
    362      if (code == PR_WOULD_BLOCK_ERROR) {
    363        return true;
    364      } else {
    365        SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
    366        return false;
    367      }
    368    }
    369  }
    370 #else
    371  status = PR_Read(mReadFD, buf, 2048);
    372  SOCKET_LOG(("PollableEvent::Clear PR_Read %d\n", status));
    373 
    374  if (status == 1) {
    375    return true;
    376  }
    377  if (status == 0) {
    378    SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
    379    return false;
    380  }
    381  if (status > 1) {
    382    MOZ_ASSERT(false);
    383    SOCKET_LOG(("PollableEvent::Clear Unexpected events\n"));
    384    Clear();
    385    return true;
    386  }
    387  PRErrorCode code = PR_GetError();
    388  if (code == PR_WOULD_BLOCK_ERROR) {
    389    return true;
    390  }
    391  SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
    392  return false;
    393 #endif  // XP_WIN
    394 }
    395 
    396 void PollableEvent::MarkFirstSignalTimestamp() {
    397  if (mFirstSignalAfterClear.IsNull()) {
    398    SOCKET_LOG(("PollableEvent::MarkFirstSignalTimestamp"));
    399    mFirstSignalAfterClear = TimeStamp::NowLoRes();
    400  }
    401 }
    402 
    403 void PollableEvent::AdjustFirstSignalTimestamp() {
    404  if (!mSignalTimestampAdjusted && !mFirstSignalAfterClear.IsNull()) {
    405    SOCKET_LOG(("PollableEvent::AdjustFirstSignalTimestamp"));
    406    mFirstSignalAfterClear = TimeStamp::NowLoRes();
    407    mSignalTimestampAdjusted = true;
    408  }
    409 }
    410 
    411 bool PollableEvent::IsSignallingAlive(TimeDuration const& timeout) {
    412  if (mWriteFailed) {
    413    return false;
    414  }
    415 
    416 #ifdef DEBUG
    417  // The timeout would be just a disturbance in a debug build.
    418  return true;
    419 #else
    420  if (!mSignaled || mFirstSignalAfterClear.IsNull() ||
    421      timeout == TimeDuration()) {
    422    return true;
    423  }
    424 
    425  TimeDuration delay = (TimeStamp::NowLoRes() - mFirstSignalAfterClear);
    426  bool timedOut = delay > timeout;
    427 
    428  return !timedOut;
    429 #endif  // DEBUG
    430 }
    431 
    432 }  // namespace net
    433 }  // namespace mozilla