Bug 698882 - backout threadevent changes due to crashes with McAfee Firefox Hook r=backout

This commit is contained in:
Patrick McManus 2016-02-21 17:41:21 -05:00
parent 06d3088237
commit 4033d4ec3c
6 changed files with 73 additions and 272 deletions

View File

@ -1,163 +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"
#ifdef XP_WIN
#include "ShutdownLayer.h"
#else
#include <fcntl.h>
#define USEPIPE 1
#endif
namespace mozilla {
namespace net {
PollableEvent::PollableEvent()
: mWriteFD(nullptr)
, mReadFD(nullptr)
, mSignaled(false)
{
// 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
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
PRFileDesc *fd[2];
if (PR_NewTCPSocketPair(fd) == PR_SUCCESS) {
mReadFD = fd[0];
mWriteFD = fd[1];
PRSocketOptionData opt;
DebugOnly<PRStatus> status;
opt.option = PR_SockOpt_NoDelay;
opt.value.no_delay = true;
PR_SetSocketOption(mWriteFD, &opt);
PR_SetSocketOption(mReadFD, &opt);
opt.option = PR_SockOpt_Nonblocking;
opt.value.non_blocking = true;
status = PR_SetSocketOption(mWriteFD, &opt);
MOZ_ASSERT(status == PR_SUCCESS);
status = PR_SetSocketOption(mReadFD, &opt);
MOZ_ASSERT(status == PR_SUCCESS);
} 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()
{
if (mWriteFD) {
#if defined(XP_WIN)
mozilla::net::AttachShutdownLayer(mWriteFD);
#endif
PR_Close(mWriteFD);
}
if (mReadFD) {
#if defined(XP_WIN)
mozilla::net::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);
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);
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

View File

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

View File

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

View File

@ -89,13 +89,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)
@ -133,6 +133,9 @@ nsSocketTransportService::~nsSocketTransportService()
{
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
NS_ASSERTION(!mInitialized, "not shutdown properly");
if (mThreadEvent)
PR_DestroyPollableEvent(mThreadEvent);
free(mActiveList);
free(mIdleList);
@ -434,7 +437,7 @@ nsSocketTransportService::PollTimeout()
}
int32_t
nsSocketTransportService::Poll(uint32_t *interval,
nsSocketTransportService::Poll(bool wait, uint32_t *interval,
TimeDuration *pollDuration)
{
PRPollDesc *pollList;
@ -442,16 +445,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...
@ -460,10 +458,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;
@ -515,20 +515,19 @@ nsSocketTransportService::Init()
if (mShuttingDown)
return NS_ERROR_UNEXPECTED;
if (!mPollableEvent) {
mPollableEvent.reset(new PollableEvent());
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 (it creates
// 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 (!mPollableEvent->Valid()) {
mPollableEvent = nullptr;
if (!mThreadEvent) {
NS_WARNING("running socket transport thread without a pollable event");
SOCKET_LOG(("running socket transport thread without a pollable event"));
}
@ -587,9 +586,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
@ -640,9 +639,8 @@ nsSocketTransportService::SetOffline(bool offline)
else if (mOffline && !offline) {
mOffline = false;
}
if (mPollableEvent) {
mPollableEvent->Signal();
}
if (mThreadEvent)
PR_SetPollableEvent(mThreadEvent);
return NS_OK;
}
@ -765,19 +763,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;
}
@ -824,15 +812,15 @@ nsSocketTransportService::Run()
gSocketThread = PR_GetCurrentThread();
// add thread event to poll list (mPollableEvent may be nullptr)
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
@ -861,6 +849,7 @@ nsSocketTransportService::Run()
for (;;) {
bool pendingEvents = false;
thread->HasPendingEvents(&pendingEvents);
numberOfPendingEvents = 0;
numberOfPendingEventsLastCycle = 0;
@ -875,7 +864,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,
@ -887,7 +878,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,
@ -911,10 +906,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() <
@ -972,7 +967,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;
@ -1013,11 +1008,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
//
@ -1072,14 +1068,15 @@ nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration)
// Measures seconds spent while blocked on PR_Poll
uint32_t pollInterval;
int32_t n = 0;
#if !defined(MOZILLA_XPCOMRT_API)
if (!gIOService->IsNetTearingDown()) {
// Let's not do polling during shutdown.
n = Poll(&pollInterval, pollDuration);
n = Poll(wait, &pollInterval, pollDuration);
}
#else
n = Poll(&pollInterval, pollDuration);
n = Poll(wait, &pollInterval, pollDuration);
#endif // defined(MOZILLA_XPCOMRT_API)
if (n < 0) {
@ -1135,28 +1132,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();
}
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;
}
}

View File

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

View File

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