Bug 698882 - backout 34046c232ee1 r=backout
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 23 Mar 2016 13:06:02 -0400
changeset 290089 bbc95a357cfdfd7226945d817a88d21675b1824f
parent 290088 6efe1b395bcf7918a752cf46a1b5725c31693ba6
child 290090 d6ccfd400a7f77151c888a975412b1532dc3df96
push id30114
push usercbook@mozilla.com
push dateThu, 24 Mar 2016 15:15:54 +0000
treeherdermozilla-central@24c5fbde4488 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs698882
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 698882 - backout 34046c232ee1 r=backout
netwerk/base/PollableEvent.cpp
netwerk/base/PollableEvent.h
netwerk/base/moz.build
netwerk/base/nsSocketTransportService2.cpp
netwerk/base/nsSocketTransportService2.h
netwerk/standalone/moz.build
deleted file mode 100644
--- a/netwerk/base/PollableEvent.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-/* -*- 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 "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
-
-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[])
-{
-  // 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.
-
-  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;
-  }
-  PR_SetSocketOption(listener, &recvBufferOpt);
-  PR_SetSocketOption(listener, &nodelayOpt);
-  PR_SetSocketOption(listener, &noblockOpt);
-  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;
-  }
-  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_INTERVAL_NO_TIMEOUT);
-  if (!reader) {
-    goto failed;
-  }
-  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();
-  if (NewTCPSocketPair(fd)) {
-    mReadFD = fd[0];
-    mWriteFD = fd[1];
-
-    // 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;
-  }
-  if (PR_GetCurrentThread() == gSocketThread) {
-    SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n"));
-    return true;
-  }
-  if (mSignaled) {
-    return true;
-  }
-  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"));
-  }
-  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 = 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;
-}
-
-} // namespace net
-} // namespace mozilla
deleted file mode 100644
--- a/netwerk/base/PollableEvent.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- 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/. */
-
-#ifndef PollableEvent_h__
-#define PollableEvent_h__
-
-#include "mozilla/Mutex.h"
-
-namespace mozilla {
-namespace net {
-
-// class must be called locked
-class PollableEvent
-{
-public:
-  PollableEvent();
-  ~PollableEvent();
-
-  // Signal/Clear return false only if they fail
-  bool Signal();
-  bool Clear();
-  bool Valid() { return mWriteFD && mReadFD; }
-
-  PRFileDesc *PollableFD() { return mReadFD; }
-
-private:
-  PRFileDesc *mWriteFD;
-  PRFileDesc *mReadFD;
-  bool        mSignaled;
-};
-
-} // namespace net
-} // namespace mozilla
-
-#endif
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -245,17 +245,16 @@ UNIFIED_SOURCES += [
     'nsSyncStreamListener.cpp',
     'nsTemporaryFileInputStream.cpp',
     'nsTransportUtils.cpp',
     'nsUDPSocket.cpp',
     'nsUnicharStreamLoader.cpp',
     'nsURLHelper.cpp',
     'nsURLParsers.cpp',
     'OfflineObserver.cpp',
-    'PollableEvent.cpp',
     'Predictor.cpp',
     'ProxyAutoConfig.cpp',
     'RedirectChannelRegistrar.cpp',
     'SchedulingContextService.cpp',
     'SimpleBuffer.cpp',
     'StreamingProtocolService.cpp',
     'Tickler.cpp',
     'TLSServerSocket.cpp',
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -82,23 +82,23 @@ DebugMutexAutoLock::~DebugMutexAutoLock(
   SOCKET_LOG(("Released lock on thread %p", PR_GetCurrentThread()));
 }
 
 //-----------------------------------------------------------------------------
 // ctor/dtor (called on the main/UI thread by the service manager)
 
 nsSocketTransportService::nsSocketTransportService()
     : mThread(nullptr)
+    , mThreadEvent(nullptr)
     , mAutodialEnabled(false)
     , mLock("nsSocketTransportService::mLock")
     , mInitialized(false)
     , mShuttingDown(false)
     , mOffline(false)
     , mGoingOffline(false)
-    , mRawThread(nullptr)
     , mActiveListSize(SOCKET_LIMIT_MIN)
     , mIdleListSize(SOCKET_LIMIT_MIN)
     , mActiveCount(0)
     , mIdleCount(0)
     , mSentBytesCount(0)
     , mReceivedBytesCount(0)
     , mEventQueueLock("nsSocketTransportService::mEventQueueLock")
     , mPendingSocketQ(mEventQueueLock)
@@ -126,16 +126,19 @@ nsSocketTransportService::nsSocketTransp
     NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
     gSocketTransportService = this;
 }
 
 nsSocketTransportService::~nsSocketTransportService()
 {
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
     NS_ASSERTION(!mInitialized, "not shutdown properly");
+    
+    if (mThreadEvent)
+        PR_DestroyPollableEvent(mThreadEvent);
 
     free(mActiveList);
     free(mIdleList);
     free(mPollList);
     gSocketTransportService = nullptr;
 }
 
 //-----------------------------------------------------------------------------
@@ -427,46 +430,43 @@ nsSocketTransportService::PollTimeout()
         SOCKET_LOG(("poll timeout: none\n"));
         return NS_SOCKET_POLL_TIMEOUT;
     }
     SOCKET_LOG(("poll timeout: %lu\n", minR));
     return PR_SecondsToInterval(minR);
 }
 
 int32_t
-nsSocketTransportService::Poll(uint32_t *interval,
+nsSocketTransportService::Poll(bool wait, uint32_t *interval,
                                TimeDuration *pollDuration)
 {
     PRPollDesc *pollList;
     uint32_t pollCount;
     PRIntervalTime pollTimeout;
     *pollDuration = 0;
 
-    // If there are pending events for this thread then
-    // DoPollIteration() should service the network without blocking.
-    bool pendingEvents = false;
-    mRawThread->HasPendingEvents(&pendingEvents);
-
     if (mPollList[0].fd) {
         mPollList[0].out_flags = 0;
         pollList = mPollList;
         pollCount = mActiveCount + 1;
-        pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout();
+        pollTimeout = PollTimeout();
     }
     else {
         // no pollable event, so busy wait...
         pollCount = mActiveCount;
         if (pollCount)
             pollList = &mPollList[1];
         else
             pollList = nullptr;
-        pollTimeout =
-            pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25);
+        pollTimeout = PR_MillisecondsToInterval(25);
     }
 
+    if (!wait)
+        pollTimeout = PR_INTERVAL_NO_WAIT;
+
     PRIntervalTime ts = PR_IntervalNow();
 
     TimeStamp pollStart;
     if (mTelemetryEnabledPref) {
         pollStart = TimeStamp::NowLoRes();
     }
 
     SOCKET_LOG(("    timeout = %i milliseconds\n",
@@ -508,16 +508,34 @@ nsSocketTransportService::Init()
     }
 
     if (mInitialized)
         return NS_OK;
 
     if (mShuttingDown)
         return NS_ERROR_UNEXPECTED;
 
+    if (!mThreadEvent) {
+        mThreadEvent = PR_NewPollableEvent();
+        //
+        // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
+        // or similar software.
+        //
+        // NOTE: per bug 191739, this failure could also be caused by lack
+        // of a loopback device on Windows and OS/2 platforms (NSPR creates
+        // a loopback socket pair on these platforms to implement a pollable
+        // event object).  if we can't create a pollable event, then we'll
+        // have to "busy wait" to implement the socket event queue :-(
+        //
+        if (!mThreadEvent) {
+            NS_WARNING("running socket transport thread without a pollable event");
+            SOCKET_LOG(("running socket transport thread without a pollable event"));
+        }
+    }
+
     nsCOMPtr<nsIThread> thread;
     nsresult rv = NS_NewThread(getter_AddRefs(thread), this);
     if (NS_FAILED(rv)) return rv;
     
     {
         DebugMutexAutoLock lock(mLock);
         // Install our mThread, protecting against concurrent readers
         thread.swap(mThread);
@@ -561,19 +579,19 @@ nsSocketTransportService::Shutdown()
         return NS_ERROR_UNEXPECTED;
 
     {
         DebugMutexAutoLock lock(mLock);
 
         // signal the socket thread to shutdown
         mShuttingDown = true;
 
-        if (mPollableEvent) {
-            mPollableEvent->Signal();
-        }
+        if (mThreadEvent)
+            PR_SetPollableEvent(mThreadEvent);
+        // else wait for Poll timeout
     }
 
     // join with thread
     mThread->Shutdown();
     {
         DebugMutexAutoLock lock(mLock);
         // Drop our reference to mThread and make sure that any concurrent
         // readers are excluded
@@ -612,19 +630,18 @@ nsSocketTransportService::SetOffline(boo
     if (!mOffline && offline) {
         // signal the socket thread to go offline, so it will detach sockets
         mGoingOffline = true;
         mOffline = true;
     }
     else if (mOffline && !offline) {
         mOffline = false;
     }
-    if (mPollableEvent) {
-        mPollableEvent->Signal();
-    }
+    if (mThreadEvent)
+        PR_SetPollableEvent(mThreadEvent);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS)
 {
     MOZ_ASSERT(aKeepaliveIdleTimeS);
@@ -727,29 +744,19 @@ nsSocketTransportService::SetAutodialEna
 {
     mAutodialEnabled = value;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
 {
-    if (PR_GetCurrentThread() == gSocketThread) {
-        // this check is redundant to one done inside ::Signal(), but
-        // we can do it here and skip obtaining the lock - given that
-        // this is a relatively common occurance its worth the
-        // redundant code
-        SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
-        return NS_OK;
-    }
-
     DebugMutexAutoLock lock(mLock);
-    if (mPollableEvent) {
-        mPollableEvent->Signal();
-    }
+    if (mThreadEvent)
+        PR_SetPollableEvent(mThreadEvent);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
                                              bool mayWait)
 {
     return NS_OK;
@@ -784,41 +791,25 @@ nsSocketTransportService::Run()
 #endif
 
     SOCKET_LOG(("STS thread init\n"));
 
     psm::InitializeSSLServerCertVerificationThreads();
 
     gSocketThread = PR_GetCurrentThread();
 
-    mPollableEvent.reset(new PollableEvent());
-    //
-    // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
-    // or similar software.
-    //
-    // NOTE: per bug 191739, this failure could also be caused by lack
-    // of a loopback device on Windows and OS/2 platforms (it creates
-    // a loopback socket pair on these platforms to implement a pollable
-    // event object).  if we can't create a pollable event, then we'll
-    // have to "busy wait" to implement the socket event queue :-(
-    //
-    if (!mPollableEvent->Valid()) {
-        mPollableEvent = nullptr;
-        NS_WARNING("running socket transport thread without a pollable event");
-        SOCKET_LOG(("running socket transport thread without a pollable event"));
-    }
-
-    mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
-    mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
+    // add thread event to poll list (mThreadEvent may be nullptr)
+    mPollList[0].fd = mThreadEvent;
+    mPollList[0].in_flags = PR_POLL_READ;
     mPollList[0].out_flags = 0;
 
-    mRawThread = NS_GetCurrentThread();
+    nsIThread *thread = NS_GetCurrentThread();
 
     // hook ourselves up to observe event processing for this thread
-    nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(mRawThread);
+    nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
     threadInt->SetObserver(this);
 
     // make sure the pseudo random number generator is seeded on this thread
     srand(static_cast<unsigned>(PR_Now()));
 
     // For the calculation of the duration of the last cycle (i.e. the last for-loop
     // iteration before shutdown).
     TimeStamp startOfCycleForLastCycleCalc;
@@ -837,43 +828,50 @@ nsSocketTransportService::Run()
 
     // If there is too many pending events queued, we will run some poll()
     // between them and the following variable is cumulative time spent
     // blocking in poll().
     TimeDuration pollDuration;
 
     for (;;) {
         bool pendingEvents = false;
+        thread->HasPendingEvents(&pendingEvents);
 
         numberOfPendingEvents = 0;
         numberOfPendingEventsLastCycle = 0;
         if (mTelemetryEnabledPref) {
             startOfCycleForLastCycleCalc = TimeStamp::NowLoRes();
             startOfNextIteration = TimeStamp::NowLoRes();
         }
         pollDuration = 0;
 
         do {
             if (mTelemetryEnabledPref) {
                 pollCycleStart = TimeStamp::NowLoRes();
             }
 
-            DoPollIteration(&singlePollDuration);
+            // If there are pending events for this thread then
+            // DoPollIteration() should service the network without blocking.
+            DoPollIteration(!pendingEvents, &singlePollDuration);
 
             if (mTelemetryEnabledPref && !pollCycleStart.IsNull()) {
                 Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME,
                                       singlePollDuration.ToMilliseconds());
                 Telemetry::AccumulateTimeDelta(
                     Telemetry::STS_POLL_CYCLE,
                     pollCycleStart + singlePollDuration,
                     TimeStamp::NowLoRes());
                 pollDuration += singlePollDuration;
             }
 
-            mRawThread->HasPendingEvents(&pendingEvents);
+            // If nothing was pending before the poll, it might be now
+            if (!pendingEvents) {
+                thread->HasPendingEvents(&pendingEvents);
+            }
+
             if (pendingEvents) {
                 if (!mServingPendingQueue) {
                     nsresult rv = Dispatch(NS_NewRunnableMethod(this,
                         &nsSocketTransportService::MarkTheLastElementOfPendingQueue),
                         nsIEventTarget::DISPATCH_NORMAL);
                     if (NS_FAILED(rv)) {
                         NS_WARNING("Could not dispatch a new event on the "
                                    "socket thread.");
@@ -887,20 +885,20 @@ nsSocketTransportService::Run()
                         // be served in the next iteration. If no even
                         // arrives, startOfNextIteration will be reset at the
                         // beginning of each for-loop.
                         startOfNextIteration = TimeStamp::NowLoRes();
                     }
                 }
                 TimeStamp eventQueueStart = TimeStamp::NowLoRes();
                 do {
-                    NS_ProcessNextEvent(mRawThread);
+                    NS_ProcessNextEvent(thread);
                     numberOfPendingEvents++;
                     pendingEvents = false;
-                    mRawThread->HasPendingEvents(&pendingEvents);
+                    thread->HasPendingEvents(&pendingEvents);
                 } while (pendingEvents && mServingPendingQueue &&
                          ((TimeStamp::NowLoRes() -
                            eventQueueStart).ToMilliseconds() <
                           mMaxTimePerPollIter));
 
                 if (mTelemetryEnabledPref && !mServingPendingQueue &&
                     !startOfIteration.IsNull()) {
                     Telemetry::AccumulateTimeDelta(
@@ -948,17 +946,17 @@ nsSocketTransportService::Run()
 
     SOCKET_LOG(("STS shutting down thread\n"));
 
     // detach all sockets, including locals
     Reset(false);
 
     // Final pass over the event queue. This makes sure that events posted by
     // socket detach handlers get processed.
-    NS_ProcessPendingEvents(mRawThread);
+    NS_ProcessPendingEvents(thread);
 
     gSocketThread = nullptr;
 
     psm::StopSSLServerCertVerificationThreads();
 
     SOCKET_LOG(("STS thread exit\n"));
     return NS_OK;
 }
@@ -987,21 +985,22 @@ nsSocketTransportService::Reset(bool aGu
         DetachSocketWithGuard(aGuardLocals, mActiveList, i);
     }
     for (i = mIdleCount - 1; i >= 0; --i) {
         DetachSocketWithGuard(aGuardLocals, mIdleList, i);
     }
 }
 
 nsresult
-nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration)
+nsSocketTransportService::DoPollIteration(bool wait, TimeDuration *pollDuration)
 {
-    SOCKET_LOG(("STS poll iter\n"));
+    SOCKET_LOG(("STS poll iter [%d]\n", wait));
 
     int32_t i, count;
+
     //
     // poll loop
     //
     // walk active list backwards to see if any sockets should actually be
     // idle, then walk the idle list backwards to see if any idle sockets
     // should become active.  take care to check only idle sockets that
     // were idle to begin with ;-)
     //
@@ -1050,17 +1049,17 @@ nsSocketTransportService::DoPollIteratio
 #endif
 
     // Measures seconds spent while blocked on PR_Poll
     uint32_t pollInterval = 0;
     int32_t n = 0;
     *pollDuration = 0;
     if (!gIOService->IsNetTearingDown()) {
         // Let's not do polling during shutdown.
-        n = Poll(&pollInterval, pollDuration);
+        n = Poll(wait, &pollInterval, pollDuration);
     }
 
     if (n < 0) {
         SOCKET_LOG(("  PR_Poll error [%d] os error [%d]\n", PR_GetError(),
                     PR_GetOSError()));
     }
     else {
         //
@@ -1106,38 +1105,39 @@ nsSocketTransportService::DoPollIteratio
         // check for "dead" sockets and remove them (need to do this in
         // reverse order obviously).
         //
         for (i=mActiveCount-1; i>=0; --i) {
             if (NS_FAILED(mActiveList[i].mHandler->mCondition))
                 DetachSocket(mActiveList, &mActiveList[i]);
         }
 
-        if (n != 0 && (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT))) {
-            DebugMutexAutoLock lock(mLock);
-
-            // acknowledge pollable event (should not block)
-            if (mPollableEvent &&
-                ((mPollList[0].out_flags & PR_POLL_EXCEPT) ||
-                 !mPollableEvent->Clear())) {
+        if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) {
+            // acknowledge pollable event (wait should not block)
+            if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) {
                 // On Windows, the TCP loopback connection in the
                 // pollable event may become broken when a laptop
                 // switches between wired and wireless networks or
                 // wakes up from hibernation.  We try to create a
                 // new pollable event.  If that fails, we fall back
                 // on "busy wait".
-                NS_WARNING("Trying to repair mPollableEvent");
-                mPollableEvent.reset(new PollableEvent());
-                if (!mPollableEvent->Valid()) {
-                    mPollableEvent = nullptr;
+                {
+                    DebugMutexAutoLock lock(mLock);
+                    PR_DestroyPollableEvent(mThreadEvent);
+                    mThreadEvent = PR_NewPollableEvent();
                 }
-                SOCKET_LOG(("running socket transport thread without "
-                            "a pollable event now valid=%d", mPollableEvent->Valid()));
-                mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
-                mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
+                if (!mThreadEvent) {
+                    NS_WARNING("running socket transport thread without "
+                               "a pollable event");
+                    SOCKET_LOG(("running socket transport thread without "
+                         "a pollable event"));
+                }
+                mPollList[0].fd = mThreadEvent;
+                // mPollList[0].in_flags was already set to PR_POLL_READ
+                // in Run().
                 mPollList[0].out_flags = 0;
             }
         }
     }
 
     return NS_OK;
 }
 
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -14,18 +14,16 @@
 #include "prinrval.h"
 #include "mozilla/Logging.h"
 #include "prinit.h"
 #include "nsIObserver.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/net/DashboardTypes.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/TimeStamp.h"
-#include "mozilla/UniquePtr.h"
-#include "PollableEvent.h"
 
 class nsASocketHandler;
 struct PRPollDesc;
 
 //-----------------------------------------------------------------------------
 
 //
 // set NSPR_LOG_MODULES=nsSocketTransport:5
@@ -121,17 +119,24 @@ protected:
 
 private:
 
     //-------------------------------------------------------------------------
     // misc (any thread)
     //-------------------------------------------------------------------------
 
     nsCOMPtr<nsIThread> mThread;    // protected by mLock
-    mozilla::UniquePtr<mozilla::net::PollableEvent> mPollableEvent;
+    PRFileDesc *mThreadEvent;
+                            // protected by mLock.  mThreadEvent may change
+                            // if the old pollable event is broken.  only
+                            // the socket thread may change mThreadEvent;
+                            // it needs to lock mLock only when it changes
+                            // mThreadEvent.  other threads don't change
+                            // mThreadEvent; they need to lock mLock
+                            // whenever they access mThreadEvent.
     bool        mAutodialEnabled;
                             // pref to control autodial code
 
     // Returns mThread, protecting the get-and-addref with mLock
     already_AddRefed<nsIThread> GetThreadSafely();
 
     //-------------------------------------------------------------------------
     // initialization and shutdown (any thread)
@@ -163,17 +168,16 @@ private:
     {
         PRFileDesc       *mFD;
         nsASocketHandler *mHandler;
         uint16_t          mElapsedTime;  // time elapsed w/o activity
     };
 
     SocketContext *mActiveList;                   /* mListSize entries */
     SocketContext *mIdleList;                     /* mListSize entries */
-    nsIThread     *mRawThread;
 
     uint32_t mActiveListSize;
     uint32_t mIdleListSize;
     uint32_t mActiveCount;
     uint32_t mIdleCount;
 
     nsresult DetachSocket(SocketContext *, SocketContext *);
     nsresult AddToIdleList(SocketContext *);
@@ -188,26 +192,28 @@ private:
     void   InitMaxCount();
 
     // Total bytes number transfered through all the sockets except active ones
     uint64_t mSentBytesCount;
     uint64_t mReceivedBytesCount;
     //-------------------------------------------------------------------------
     // poll list (socket thread only)
     //
-    // first element of the poll list is mPollableEvent (or null if the pollable
+    // first element of the poll list is mThreadEvent (or null if the pollable
     // event cannot be created).
     //-------------------------------------------------------------------------
 
     PRPollDesc *mPollList;                        /* mListSize + 1 entries */
 
     PRIntervalTime PollTimeout();            // computes ideal poll timeout
-    nsresult       DoPollIteration(mozilla::TimeDuration *pollDuration);
+    nsresult       DoPollIteration(bool wait,
+                                   mozilla::TimeDuration *pollDuration);
                                              // perfoms a single poll iteration
-    int32_t        Poll(uint32_t *interval,
+    int32_t        Poll(bool wait,
+                        uint32_t *interval,
                         mozilla::TimeDuration *pollDuration);
                                              // calls PR_Poll.  the out param
                                              // interval indicates the poll
                                              // duration in seconds.
                                              // pollDuration is used only for
                                              // telemetry
 
     //-------------------------------------------------------------------------
--- a/netwerk/standalone/moz.build
+++ b/netwerk/standalone/moz.build
@@ -7,17 +7,16 @@
 if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     Library('necko_standalone')
 
 src_list = [
     'nsNetModuleStandalone.cpp',
 ]
 
 netwerk_base_src = [
-    'PollableEvent.cpp',
     'nsDNSPrefetch.cpp',
     'nsNetAddr.cpp',
     'nsSocketTransportService2.cpp',
     'nsURLHelper.cpp',
 ]
 src_list += [
     '/netwerk/base/%s' % s for s in netwerk_base_src
 ]