LCOV - code coverage report
Current view: top level - netwerk/base - PollableEvent.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 37 66 56.1 %
Date: 2017-07-14 16:53:18 Functions: 3 4 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "prerror.h"
      13             : #include "prio.h"
      14             : #include "private/pprio.h"
      15             : #include "prnetdb.h"
      16             : 
      17             : #ifdef XP_WIN
      18             : #include "ShutdownLayer.h"
      19             : #else
      20             : #include <fcntl.h>
      21             : #define USEPIPE 1
      22             : #endif
      23             : 
      24             : namespace mozilla {
      25             : namespace net {
      26             : 
      27             : #ifndef USEPIPE
      28             : static PRDescIdentity sPollableEventLayerIdentity;
      29             : static PRIOMethods    sPollableEventLayerMethods;
      30             : static PRIOMethods   *sPollableEventLayerMethodsPtr = nullptr;
      31             : 
      32             : static void LazyInitSocket()
      33             : {
      34             :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
      35             :   if (sPollableEventLayerMethodsPtr) {
      36             :     return;
      37             :   }
      38             :   sPollableEventLayerIdentity = PR_GetUniqueIdentity("PollableEvent Layer");
      39             :   sPollableEventLayerMethods = *PR_GetDefaultIOMethods();
      40             :   sPollableEventLayerMethodsPtr = &sPollableEventLayerMethods;
      41             : }
      42             : 
      43             : static bool NewTCPSocketPair(PRFileDesc *fd[], bool aSetRecvBuff)
      44             : {
      45             :   // this is a replacement for PR_NewTCPSocketPair that manually
      46             :   // sets the recv buffer to 64K. A windows bug (1248358)
      47             :   // can result in using an incompatible rwin and window
      48             :   // scale option on localhost pipes if not set before connect.
      49             : 
      50             :   SOCKET_LOG(("NewTCPSocketPair %s a recv buffer tuning\n", aSetRecvBuff ? "with" : "without"));
      51             : 
      52             :   PRFileDesc *listener = nullptr;
      53             :   PRFileDesc *writer = nullptr;
      54             :   PRFileDesc *reader = nullptr;
      55             :   PRSocketOptionData recvBufferOpt;
      56             :   recvBufferOpt.option = PR_SockOpt_RecvBufferSize;
      57             :   recvBufferOpt.value.recv_buffer_size = 65535;
      58             : 
      59             :   PRSocketOptionData nodelayOpt;
      60             :   nodelayOpt.option = PR_SockOpt_NoDelay;
      61             :   nodelayOpt.value.no_delay = true;
      62             : 
      63             :   PRSocketOptionData noblockOpt;
      64             :   noblockOpt.option = PR_SockOpt_Nonblocking;
      65             :   noblockOpt.value.non_blocking = true;
      66             : 
      67             :   listener = PR_OpenTCPSocket(PR_AF_INET);
      68             :   if (!listener) {
      69             :     goto failed;
      70             :   }
      71             : 
      72             :   if (aSetRecvBuff) {
      73             :     PR_SetSocketOption(listener, &recvBufferOpt);
      74             :   }
      75             :   PR_SetSocketOption(listener, &nodelayOpt);
      76             : 
      77             :   PRNetAddr listenAddr;
      78             :   memset(&listenAddr, 0, sizeof(listenAddr));
      79             :   if ((PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &listenAddr) == PR_FAILURE) ||
      80             :       (PR_Bind(listener, &listenAddr) == PR_FAILURE) ||
      81             :       (PR_GetSockName(listener, &listenAddr) == PR_FAILURE) || // learn the dynamic port
      82             :       (PR_Listen(listener, 5) == PR_FAILURE)) {
      83             :     goto failed;
      84             :   }
      85             : 
      86             :   writer = PR_OpenTCPSocket(PR_AF_INET);
      87             :   if (!writer) {
      88             :     goto failed;
      89             :   }
      90             :   if (aSetRecvBuff) {
      91             :     PR_SetSocketOption(writer, &recvBufferOpt);
      92             :   }
      93             :   PR_SetSocketOption(writer, &nodelayOpt);
      94             :   PR_SetSocketOption(writer, &noblockOpt);
      95             :   PRNetAddr writerAddr;
      96             :   if (PR_InitializeNetAddr(PR_IpAddrLoopback, ntohs(listenAddr.inet.port), &writerAddr) == PR_FAILURE) {
      97             :     goto failed;
      98             :   }
      99             : 
     100             :   if (PR_Connect(writer, &writerAddr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
     101             :     if ((PR_GetError() != PR_IN_PROGRESS_ERROR) ||
     102             :         (PR_ConnectContinue(writer, PR_POLL_WRITE) == PR_FAILURE)) {
     103             :       goto failed;
     104             :     }
     105             :   }
     106             : 
     107             :   reader = PR_Accept(listener, &listenAddr, PR_MillisecondsToInterval(200));
     108             :   if (!reader) {
     109             :     goto failed;
     110             :   }
     111             :   if (aSetRecvBuff) {
     112             :     PR_SetSocketOption(reader, &recvBufferOpt);
     113             :   }
     114             :   PR_SetSocketOption(reader, &nodelayOpt);
     115             :   PR_SetSocketOption(reader, &noblockOpt);
     116             :   PR_Close(listener);
     117             : 
     118             :   fd[0] = reader;
     119             :   fd[1] = writer;
     120             :   return true;
     121             : 
     122             : failed:
     123             :   if (listener) {
     124             :     PR_Close(listener);
     125             :   }
     126             :   if (reader) {
     127             :     PR_Close(reader);
     128             :   }
     129             :   if (writer) {
     130             :     PR_Close(writer);
     131             :   }
     132             :   return false;
     133             : }
     134             : 
     135             : #endif
     136             : 
     137           3 : PollableEvent::PollableEvent()
     138             :   : mWriteFD(nullptr)
     139             :   , mReadFD(nullptr)
     140           3 :   , mSignaled(false)
     141             : {
     142           3 :   MOZ_COUNT_CTOR(PollableEvent);
     143           3 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     144             :   // create pair of prfiledesc that can be used as a poll()ble
     145             :   // signal. on windows use a localhost socket pair, and on
     146             :   // unix use a pipe.
     147             : #ifdef USEPIPE
     148           3 :   SOCKET_LOG(("PollableEvent() using pipe\n"));
     149           3 :   if (PR_CreatePipe(&mReadFD, &mWriteFD) == PR_SUCCESS) {
     150             :     // make the pipe non blocking. NSPR asserts at
     151             :     // trying to use SockOpt here
     152           3 :     PROsfd fd = PR_FileDesc2NativeHandle(mReadFD);
     153           3 :     int flags = fcntl(fd, F_GETFL, 0);
     154           3 :     (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
     155           3 :     fd = PR_FileDesc2NativeHandle(mWriteFD);
     156           3 :     flags = fcntl(fd, F_GETFL, 0);
     157           3 :     (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
     158             :   } else {
     159           0 :     mReadFD = nullptr;
     160           0 :     mWriteFD = nullptr;
     161           0 :     SOCKET_LOG(("PollableEvent() pipe failed\n"));
     162             :   }
     163             : #else
     164             :   SOCKET_LOG(("PollableEvent() using socket pair\n"));
     165             :   PRFileDesc *fd[2];
     166             :   LazyInitSocket();
     167             : 
     168             :   // Try with a increased recv buffer first (bug 1248358).
     169             :   if (NewTCPSocketPair(fd, true)) {
     170             :     mReadFD = fd[0];
     171             :     mWriteFD = fd[1];
     172             :   // If the previous fails try without recv buffer increase (bug 1305436).
     173             :   } else if (NewTCPSocketPair(fd, false)) {
     174             :     mReadFD = fd[0];
     175             :     mWriteFD = fd[1];
     176             :   // If both fail, try the old version.
     177             :   } else if (PR_NewTCPSocketPair(fd) == PR_SUCCESS) {
     178             :     mReadFD = fd[0];
     179             :     mWriteFD = fd[1];
     180             : 
     181             :     PRSocketOptionData socket_opt;
     182             :     DebugOnly<PRStatus> status;
     183             :     socket_opt.option = PR_SockOpt_NoDelay;
     184             :     socket_opt.value.no_delay = true;
     185             :     PR_SetSocketOption(mWriteFD, &socket_opt);
     186             :     PR_SetSocketOption(mReadFD, &socket_opt);
     187             :     socket_opt.option = PR_SockOpt_Nonblocking;
     188             :     socket_opt.value.non_blocking = true;
     189             :     status = PR_SetSocketOption(mWriteFD, &socket_opt);
     190             :     MOZ_ASSERT(status == PR_SUCCESS);
     191             :     status = PR_SetSocketOption(mReadFD, &socket_opt);
     192             :     MOZ_ASSERT(status == PR_SUCCESS);
     193             :   }
     194             : 
     195             :   if (mReadFD && mWriteFD) {
     196             :     // compatibility with LSPs such as McAfee that assume a NSPR
     197             :     // layer for read ala the nspr Pollable Event - Bug 698882. This layer is a nop.
     198             :     PRFileDesc *topLayer =
     199             :       PR_CreateIOLayerStub(sPollableEventLayerIdentity,
     200             :                            sPollableEventLayerMethodsPtr);
     201             :     if (topLayer) {
     202             :       if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, topLayer) == PR_FAILURE) {
     203             :         topLayer->dtor(topLayer);
     204             :       } else {
     205             :         SOCKET_LOG(("PollableEvent() nspr layer ok\n"));
     206             :         mReadFD = topLayer;
     207             :       }
     208             :     }
     209             : 
     210             :   } else {
     211             :     SOCKET_LOG(("PollableEvent() socketpair failed\n"));
     212             :   }
     213             : #endif
     214             : 
     215           3 :   if (mReadFD && mWriteFD) {
     216             :     // prime the system to deal with races invovled in [dc]tor cycle
     217           3 :     SOCKET_LOG(("PollableEvent() ctor ok\n"));
     218           3 :     mSignaled = true;
     219           3 :     PR_Write(mWriteFD, "I", 1);
     220             :   }
     221           3 : }
     222             : 
     223           0 : PollableEvent::~PollableEvent()
     224             : {
     225           0 :   MOZ_COUNT_DTOR(PollableEvent);
     226           0 :   if (mWriteFD) {
     227             : #if defined(XP_WIN)
     228             :     AttachShutdownLayer(mWriteFD);
     229             : #endif
     230           0 :     PR_Close(mWriteFD);
     231             :   }
     232           0 :   if (mReadFD) {
     233             : #if defined(XP_WIN)
     234             :     AttachShutdownLayer(mReadFD);
     235             : #endif
     236           0 :     PR_Close(mReadFD);
     237             :   }
     238           0 : }
     239             : 
     240             : // we do not record signals on the socket thread
     241             : // because the socket thread can reliably look at its
     242             : // own runnable queue before selecting a poll time
     243             : // this is the "service the network without blocking" comment in
     244             : // nsSocketTransportService2.cpp
     245             : bool
     246          43 : PollableEvent::Signal()
     247             : {
     248          43 :   SOCKET_LOG(("PollableEvent::Signal\n"));
     249             : 
     250          43 :   if (!mWriteFD) {
     251           0 :     SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n"));
     252           0 :     return false;
     253             :   }
     254             : #ifndef XP_WIN
     255             :   // On windows poll can hang and this became worse when we introduced the
     256             :   // patch for bug 698882 (see also bug 1292181), therefore we reverted the
     257             :   // behavior on windows to be as before bug 698882, e.g. write to the socket
     258             :   // also if an event dispatch is on the socket thread and writing to the
     259             :   // socket for each event. See bug 1292181.
     260          43 :   if (OnSocketThread()) {
     261           0 :     SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n"));
     262           0 :     return true;
     263             :   }
     264             : #endif
     265             : 
     266             : #ifndef XP_WIN
     267             :   // To wake up the poll writing once is enough, but for Windows that can cause
     268             :   // hangs so we will write for every event.
     269             :   // For non-Windows systems it is enough to write just once.
     270          43 :   if (mSignaled) {
     271           6 :     return true;
     272             :   }
     273             : #endif
     274             : 
     275          37 :   mSignaled = true;
     276          37 :   int32_t status = PR_Write(mWriteFD, "M", 1);
     277          37 :   SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status));
     278          37 :   if (status != 1) {
     279           0 :     NS_WARNING("PollableEvent::Signal Failed\n");
     280           0 :     SOCKET_LOG(("PollableEvent::Signal Failed\n"));
     281           0 :     mSignaled = false;
     282             :   }
     283          37 :   return (status == 1);
     284             : }
     285             : 
     286             : bool
     287          40 : PollableEvent::Clear()
     288             : {
     289             :   // necessary because of the "dont signal on socket thread" optimization
     290          40 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     291             : 
     292          40 :   SOCKET_LOG(("PollableEvent::Clear\n"));
     293          40 :   mSignaled = false;
     294          40 :   if (!mReadFD) {
     295           0 :     SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n"));
     296           0 :     return false;
     297             :   }
     298             :   char buf[2048];
     299             :   int32_t status;
     300             : #ifdef XP_WIN
     301             :   // On Windows we are writing to the socket for each event, to be sure that we
     302             :   // do not have any deadlock read from the socket as much as we can.
     303             :   while (true) {
     304             :     status = PR_Read(mReadFD, buf, 2048);
     305             :     SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status));
     306             :     if (status == 0) {
     307             :       SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
     308             :       return false;
     309             :     }
     310             :     if (status < 0) {
     311             :       PRErrorCode code = PR_GetError();
     312             :       if (code == PR_WOULD_BLOCK_ERROR) {
     313             :         return true;
     314             :       } else {
     315             :         SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
     316             :         return false;
     317             :       }
     318             :     }
     319             :   }
     320             : #else
     321          40 :   status = PR_Read(mReadFD, buf, 2048);
     322          40 :   SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status));
     323             : 
     324          40 :   if (status == 1) {
     325          40 :     return true;
     326             :   }
     327           0 :   if (status == 0) {
     328           0 :     SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
     329           0 :     return false;
     330             :   }
     331           0 :   if (status > 1) {
     332           0 :     MOZ_ASSERT(false);
     333             :     SOCKET_LOG(("PollableEvent::Clear Unexpected events\n"));
     334             :     Clear();
     335             :     return true;
     336             :   }
     337           0 :   PRErrorCode code = PR_GetError();
     338           0 :   if (code == PR_WOULD_BLOCK_ERROR) {
     339           0 :     return true;
     340             :   }
     341           0 :   SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
     342           0 :   return false;
     343             : #endif //XP_WIN
     344             : 
     345             : }
     346             : } // namespace net
     347             : } // namespace mozilla

Generated by: LCOV version 1.13