123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* vim:set ts=2 sw=2 sts=2 et cindent: */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsSocketTransportService2.h"
- #include "PollableEvent.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/DebugOnly.h"
- #include "mozilla/Logging.h"
- #include "mozilla/net/DNS.h"
- #include "prerror.h"
- #include "prio.h"
- #include "private/pprio.h"
- #include "prnetdb.h"
- #ifdef XP_WIN
- #include "ShutdownLayer.h"
- #else
- #include <fcntl.h>
- #define USEPIPE 1
- #endif
- using namespace mozilla::net;
- namespace mozilla {
- namespace net {
- #ifndef USEPIPE
- static PRDescIdentity sPollableEventLayerIdentity;
- static PRIOMethods sPollableEventLayerMethods;
- static PRIOMethods *sPollableEventLayerMethodsPtr = nullptr;
- static void LazyInitSocket()
- {
- MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
- if (sPollableEventLayerMethodsPtr) {
- return;
- }
- sPollableEventLayerIdentity = PR_GetUniqueIdentity("PollableEvent Layer");
- sPollableEventLayerMethods = *PR_GetDefaultIOMethods();
- sPollableEventLayerMethodsPtr = &sPollableEventLayerMethods;
- }
- static bool NewTCPSocketPair(PRFileDesc *fd[], bool aSetRecvBuff)
- {
- // this is a replacement for PR_NewTCPSocketPair that manually
- // sets the recv buffer to 64K. A windows bug (1248358)
- // can result in using an incompatible rwin and window
- // scale option on localhost pipes if not set before connect.
- SOCKET_LOG(("NewTCPSocketPair %s a recv buffer tuning\n", aSetRecvBuff ? "with" : "without"));
- PRFileDesc *listener = nullptr;
- PRFileDesc *writer = nullptr;
- PRFileDesc *reader = nullptr;
- PRSocketOptionData recvBufferOpt;
- recvBufferOpt.option = PR_SockOpt_RecvBufferSize;
- recvBufferOpt.value.recv_buffer_size = 65535;
- PRSocketOptionData nodelayOpt;
- nodelayOpt.option = PR_SockOpt_NoDelay;
- nodelayOpt.value.no_delay = true;
- PRSocketOptionData noblockOpt;
- noblockOpt.option = PR_SockOpt_Nonblocking;
- noblockOpt.value.non_blocking = true;
- listener = PR_OpenTCPSocket(PR_AF_INET);
- if (!listener) {
- goto failed;
- }
- if (aSetRecvBuff) {
- PR_SetSocketOption(listener, &recvBufferOpt);
- }
- PR_SetSocketOption(listener, &nodelayOpt);
- PRNetAddr listenAddr;
- memset(&listenAddr, 0, sizeof(listenAddr));
- if ((PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &listenAddr) == PR_FAILURE) ||
- (PR_Bind(listener, &listenAddr) == PR_FAILURE) ||
- (PR_GetSockName(listener, &listenAddr) == PR_FAILURE) || // learn the dynamic port
- (PR_Listen(listener, 5) == PR_FAILURE)) {
- goto failed;
- }
- writer = PR_OpenTCPSocket(PR_AF_INET);
- if (!writer) {
- goto failed;
- }
- if (aSetRecvBuff) {
- PR_SetSocketOption(writer, &recvBufferOpt);
- }
- PR_SetSocketOption(writer, &nodelayOpt);
- PR_SetSocketOption(writer, &noblockOpt);
- PRNetAddr writerAddr;
- if (PR_InitializeNetAddr(PR_IpAddrLoopback, ntohs(listenAddr.inet.port), &writerAddr) == PR_FAILURE) {
- goto failed;
- }
- if (PR_Connect(writer, &writerAddr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
- if ((PR_GetError() != PR_IN_PROGRESS_ERROR) ||
- (PR_ConnectContinue(writer, PR_POLL_WRITE) == PR_FAILURE)) {
- goto failed;
- }
- }
- reader = PR_Accept(listener, &listenAddr, PR_MillisecondsToInterval(200));
- if (!reader) {
- goto failed;
- }
- if (aSetRecvBuff) {
- PR_SetSocketOption(reader, &recvBufferOpt);
- }
- PR_SetSocketOption(reader, &nodelayOpt);
- PR_SetSocketOption(reader, &noblockOpt);
- PR_Close(listener);
- fd[0] = reader;
- fd[1] = writer;
- return true;
- failed:
- if (listener) {
- PR_Close(listener);
- }
- if (reader) {
- PR_Close(reader);
- }
- if (writer) {
- PR_Close(writer);
- }
- return false;
- }
- #endif
- PollableEvent::PollableEvent()
- : mWriteFD(nullptr)
- , mReadFD(nullptr)
- , mSignaled(false)
- {
- MOZ_COUNT_CTOR(PollableEvent);
- MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
- // create pair of prfiledesc that can be used as a poll()ble
- // signal. on windows use a localhost socket pair, and on
- // unix use a pipe.
- #ifdef USEPIPE
- SOCKET_LOG(("PollableEvent() using pipe\n"));
- if (PR_CreatePipe(&mReadFD, &mWriteFD) == PR_SUCCESS) {
- // make the pipe non blocking. NSPR asserts at
- // trying to use SockOpt here
- PROsfd fd = PR_FileDesc2NativeHandle(mReadFD);
- int flags = fcntl(fd, F_GETFL, 0);
- (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- fd = PR_FileDesc2NativeHandle(mWriteFD);
- flags = fcntl(fd, F_GETFL, 0);
- (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- } else {
- mReadFD = nullptr;
- mWriteFD = nullptr;
- SOCKET_LOG(("PollableEvent() pipe failed\n"));
- }
- #else
- SOCKET_LOG(("PollableEvent() using socket pair\n"));
- PRFileDesc *fd[2];
- LazyInitSocket();
- // Try with a increased recv buffer first (bug 1248358).
- if (NewTCPSocketPair(fd, true)) {
- mReadFD = fd[0];
- mWriteFD = fd[1];
- // If the previous fails try without recv buffer increase (bug 1305436).
- } else if (NewTCPSocketPair(fd, false)) {
- mReadFD = fd[0];
- mWriteFD = fd[1];
- // If both fail, try the old version.
- } else if (PR_NewTCPSocketPair(fd) == PR_SUCCESS) {
- mReadFD = fd[0];
- mWriteFD = fd[1];
- PRSocketOptionData socket_opt;
- DebugOnly<PRStatus> status;
- socket_opt.option = PR_SockOpt_NoDelay;
- socket_opt.value.no_delay = true;
- PR_SetSocketOption(mWriteFD, &socket_opt);
- PR_SetSocketOption(mReadFD, &socket_opt);
- socket_opt.option = PR_SockOpt_Nonblocking;
- socket_opt.value.non_blocking = true;
- status = PR_SetSocketOption(mWriteFD, &socket_opt);
- MOZ_ASSERT(status == PR_SUCCESS);
- status = PR_SetSocketOption(mReadFD, &socket_opt);
- MOZ_ASSERT(status == PR_SUCCESS);
- }
- if (mReadFD && mWriteFD) {
- // compatibility with LSPs such as McAfee that assume a NSPR
- // layer for read ala the nspr Pollable Event - Bug 698882. This layer is a nop.
- PRFileDesc *topLayer =
- PR_CreateIOLayerStub(sPollableEventLayerIdentity,
- sPollableEventLayerMethodsPtr);
- if (topLayer) {
- if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, topLayer) == PR_FAILURE) {
- topLayer->dtor(topLayer);
- } else {
- SOCKET_LOG(("PollableEvent() nspr layer ok\n"));
- mReadFD = topLayer;
- }
- }
- } else {
- SOCKET_LOG(("PollableEvent() socketpair failed\n"));
- }
- #endif
- if (mReadFD && mWriteFD) {
- // prime the system to deal with races invovled in [dc]tor cycle
- SOCKET_LOG(("PollableEvent() ctor ok\n"));
- mSignaled = true;
- PR_Write(mWriteFD, "I", 1);
- }
- }
- PollableEvent::~PollableEvent()
- {
- MOZ_COUNT_DTOR(PollableEvent);
- if (mWriteFD) {
- #if defined(XP_WIN)
- AttachShutdownLayer(mWriteFD);
- #endif
- PR_Close(mWriteFD);
- }
- if (mReadFD) {
- #if defined(XP_WIN)
- AttachShutdownLayer(mReadFD);
- #endif
- PR_Close(mReadFD);
- }
- }
- // we do not record signals on the socket thread
- // because the socket thread can reliably look at its
- // own runnable queue before selecting a poll time
- // this is the "service the network without blocking" comment in
- // nsSocketTransportService2.cpp
- bool
- PollableEvent::Signal()
- {
- SOCKET_LOG(("PollableEvent::Signal\n"));
- if (!mWriteFD) {
- SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n"));
- return false;
- }
- #ifndef XP_WIN
- // On windows poll can hang and this became worse when we introduced the
- // patch for bug 698882 (see also bug 1292181), therefore we reverted the
- // behavior on windows to be as before bug 698882, e.g. write to the socket
- // also if an event dispatch is on the socket thread and writing to the
- // socket for each event. See bug 1292181.
- if (PR_GetCurrentThread() == gSocketThread) {
- SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n"));
- return true;
- }
- #endif
- #ifndef XP_WIN
- // To wake up the poll writing once is enough, but for Windows that can cause
- // hangs so we will write for every event.
- // For non-Windows systems it is enough to write just once.
- if (mSignaled) {
- return true;
- }
- #endif
- mSignaled = true;
- int32_t status = PR_Write(mWriteFD, "M", 1);
- SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status));
- if (status != 1) {
- NS_WARNING("PollableEvent::Signal Failed\n");
- SOCKET_LOG(("PollableEvent::Signal Failed\n"));
- mSignaled = false;
- }
- return (status == 1);
- }
- bool
- PollableEvent::Clear()
- {
- // necessary because of the "dont signal on socket thread" optimization
- MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
- SOCKET_LOG(("PollableEvent::Clear\n"));
- mSignaled = false;
- if (!mReadFD) {
- SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n"));
- return false;
- }
- char buf[2048];
- int32_t status;
- #ifdef XP_WIN
- // On Windows we are writing to the socket for each event, to be sure that we
- // do not have any deadlock read from the socket as much as we can.
- while (true) {
- status = PR_Read(mReadFD, buf, 2048);
- SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status));
- if (status == 0) {
- SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
- return false;
- }
- if (status < 0) {
- PRErrorCode code = PR_GetError();
- if (code == PR_WOULD_BLOCK_ERROR) {
- return true;
- } else {
- SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
- return false;
- }
- }
- }
- #else
- status = PR_Read(mReadFD, buf, 2048);
- SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status));
- if (status == 1) {
- return true;
- }
- if (status == 0) {
- SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
- return false;
- }
- if (status > 1) {
- MOZ_ASSERT(false);
- SOCKET_LOG(("PollableEvent::Clear Unexpected events\n"));
- Clear();
- return true;
- }
- PRErrorCode code = PR_GetError();
- if (code == PR_WOULD_BLOCK_ERROR) {
- return true;
- }
- SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
- return false;
- #endif //XP_WIN
- }
- } // namespace net
- } // namespace mozilla
|