Bug 698882 - backout 34046c232ee1 r=backout

This commit is contained in:
Patrick McManus 2016-03-23 13:06:02 -04:00
Родитель f1ca01f79f
Коммит 78cb6a4c07
6 изменённых файлов: 85 добавлений и 394 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -250,7 +250,6 @@ UNIFIED_SOURCES += [
'nsURLHelper.cpp',
'nsURLParsers.cpp',
'OfflineObserver.cpp',
'PollableEvent.cpp',
'Predictor.cpp',
'ProxyAutoConfig.cpp',
'RedirectChannelRegistrar.cpp',

Просмотреть файл

@ -87,13 +87,13 @@ DebugMutexAutoLock::~DebugMutexAutoLock()
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)
@ -132,6 +132,9 @@ 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);
@ -432,7 +435,7 @@ nsSocketTransportService::PollTimeout()
}
int32_t
nsSocketTransportService::Poll(uint32_t *interval,
nsSocketTransportService::Poll(bool wait, uint32_t *interval,
TimeDuration *pollDuration)
{
PRPollDesc *pollList;
@ -440,16 +443,11 @@ nsSocketTransportService::Poll(uint32_t *interval,
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...
@ -458,10 +456,12 @@ nsSocketTransportService::Poll(uint32_t *interval,
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;
@ -513,6 +513,24 @@ nsSocketTransportService::Init()
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;
@ -566,9 +584,9 @@ nsSocketTransportService::Shutdown()
// 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
@ -617,9 +635,8 @@ nsSocketTransportService::SetOffline(bool offline)
else if (mOffline && !offline) {
mOffline = false;
}
if (mPollableEvent) {
mPollableEvent->Signal();
}
if (mThreadEvent)
PR_SetPollableEvent(mThreadEvent);
return NS_OK;
}
@ -732,19 +749,9 @@ nsSocketTransportService::SetAutodialEnabled(bool value)
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;
}
@ -789,31 +796,15 @@ nsSocketTransportService::Run()
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
@ -842,6 +833,7 @@ nsSocketTransportService::Run()
for (;;) {
bool pendingEvents = false;
thread->HasPendingEvents(&pendingEvents);
numberOfPendingEvents = 0;
numberOfPendingEventsLastCycle = 0;
@ -856,7 +848,9 @@ nsSocketTransportService::Run()
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,
@ -868,7 +862,11 @@ nsSocketTransportService::Run()
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,
@ -892,10 +890,10 @@ nsSocketTransportService::Run()
}
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() <
@ -953,7 +951,7 @@ nsSocketTransportService::Run()
// 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;
@ -992,11 +990,12 @@ nsSocketTransportService::Reset(bool aGuardLocals)
}
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
//
@ -1055,7 +1054,7 @@ nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration)
*pollDuration = 0;
if (!gIOService->IsNetTearingDown()) {
// Let's not do polling during shutdown.
n = Poll(&pollInterval, pollDuration);
n = Poll(wait, &pollInterval, pollDuration);
}
if (n < 0) {
@ -1111,28 +1110,29 @@ nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration)
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();
}
if (!mThreadEvent) {
NS_WARNING("running socket transport thread without "
"a pollable event");
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;
"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;
}
}

Просмотреть файл

@ -19,8 +19,6 @@
#include "mozilla/net/DashboardTypes.h"
#include "mozilla/Atomics.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "PollableEvent.h"
class nsASocketHandler;
struct PRPollDesc;
@ -126,7 +124,14 @@ private:
//-------------------------------------------------------------------------
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
@ -168,7 +173,6 @@ private:
SocketContext *mActiveList; /* mListSize entries */
SocketContext *mIdleList; /* mListSize entries */
nsIThread *mRawThread;
uint32_t mActiveListSize;
uint32_t mIdleListSize;
@ -193,16 +197,18 @@ private:
//-------------------------------------------------------------------------
// 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

Просмотреть файл

@ -12,7 +12,6 @@ src_list = [
]
netwerk_base_src = [
'PollableEvent.cpp',
'nsDNSPrefetch.cpp',
'nsNetAddr.cpp',
'nsSocketTransportService2.cpp',