Backed out changeset efa0fb87773e (bug 698882) for failing xpcshell and xperf tests
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 20 Apr 2016 16:36:07 +0200
changeset 325923 c678a3c4169ecdb9bb761e98978307b008659246
parent 325922 88c060c38873d2373efd49a2e58acc46c22ba532
child 325924 5649dee23169c659655bfd2f9bae78b5b1bed073
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs698882
milestone47.0a2
backs outefa0fb87773ed6e6850dcaf794d53651429decca
Backed out changeset efa0fb87773e (bug 698882) for failing xpcshell and xperf tests
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
@@ -244,17 +244,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,44 +791,25 @@ nsSocketTransportService::Run()
 #endif
 
     SOCKET_LOG(("STS thread init\n"));
 
     psm::InitializeSSLServerCertVerificationThreads();
 
     gSocketThread = PR_GetCurrentThread();
 
-    {
-        DebugMutexAutoLock lock(mLock);
-        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"));
-        }
+    // 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;
 
-        mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
-        mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
-        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;
@@ -840,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.");
@@ -890,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(
@@ -951,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;
 }
@@ -990,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 ;-)
     //
@@ -1053,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 {
         //
@@ -1109,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
 ]