Bug 1363372 - Wait until OVERLAPPED structure return a result before distroying a socket. r=mcmanus

This commit is contained in:
Dragana Damjanovic 2017-07-27 11:37:11 +02:00
Родитель 77398bcd88
Коммит a7386daa3f
6 изменённых файлов: 257 добавлений и 13 удалений

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

@ -1278,8 +1278,7 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &us
SOCKET_LOG((" error pushing io layer [%u:%s rv=%" PRIx32 "]\n", i, mTypes[i],
static_cast<uint32_t>(rv)));
if (fd) {
CloseSocket(fd,
mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
CloseSocket(fd, mSocketTransportService);
}
}
}
@ -1473,8 +1472,7 @@ nsSocketTransport::InitiateSocket()
// inform socket transport about this newly created socket...
rv = mSocketTransportService->AttachSocket(fd, this);
if (NS_FAILED(rv)) {
CloseSocket(fd,
mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
CloseSocket(fd, mSocketTransportService);
return rv;
}
mAttached = true;
@ -1981,8 +1979,7 @@ public:
NS_IMETHOD Run() override
{
nsSocketTransport::CloseSocket(mFD,
gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
nsSocketTransport::CloseSocket(mFD, gSocketTransportService);
return NS_OK;
}
private:
@ -2017,8 +2014,7 @@ nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
SOCKET_LOG(("Intentional leak"));
} else if (OnSocketThread()) {
SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
CloseSocket(mFD,
mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
CloseSocket(mFD, mSocketTransportService);
} else {
// Can't PR_Close() a socket off STS thread. Thunk it to STS to die
STS_PRCloseOnSocketTransport(mFD);
@ -3437,8 +3433,10 @@ nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(bool aEnabled,
}
void
nsSocketTransport::CloseSocket(PRFileDesc *aFd, bool aTelemetryEnabled)
nsSocketTransport::CloseSocket(PRFileDesc *aFd, nsSocketTransportService *aSTS)
{
bool telemetryEnabled = aSTS->IsTelemetryEnabledAndNotSleepPhase();
#if defined(XP_WIN)
AttachShutdownLayer(aFd);
#endif
@ -3447,13 +3445,67 @@ nsSocketTransport::CloseSocket(PRFileDesc *aFd, bool aTelemetryEnabled)
// nsIOService::LastOfflineStateChange time and
// nsIOService::LastConectivityChange time to be atomic.
PRIntervalTime closeStarted;
if (aTelemetryEnabled) {
if (telemetryEnabled) {
closeStarted = PR_IntervalNow();
}
PR_Close(aFd);
#if defined(_WIN64) && defined(WIN95)
bool canClose = false;
if (aSTS->HasFileDesc2PlatformOverlappedIOHandleFunc()) {
LPOVERLAPPED ol = nullptr;
if (aSTS->CallFileDesc2PlatformOverlappedIOHandleFunc(aFd, (void**)&ol) == PR_SUCCESS) {
SOCKET_LOG(("nsSocketTransport::CloseSocket - we have an overlapped "
"structure=%p aFd=%p\n", ol, aFd));
PROsfd osfd = PR_FileDesc2NativeHandle(aFd);
if (telemetryEnabled) {
Telemetry::ScalarAdd(Telemetry::ScalarID::NETWORK_TCP_OVERLAPPED_IO_CANCELED_BEFORE_FINISHED, 1);
}
if (CancelIo((HANDLE) osfd) == TRUE) {
SOCKET_LOG(("nsSocketTransport::CloseSocket - "
"CancelIo succeeded\n"));
} else {
int err = WSAGetLastError();
SOCKET_LOG(("nsSocketTransport::CloseSocket - "
"CancelIo failed err=%x\n", err));
}
if (aTelemetryEnabled) {
DWORD rvSent;
if (GetOverlappedResult((HANDLE) osfd, ol, &rvSent, FALSE) == TRUE) {
SOCKET_LOG(("nsSocketTransport::CloseSocket - "
"GetOverlappedResult done\n"));
canClose = true;
} else {
int err = WSAGetLastError();
SOCKET_LOG(("nsSocketTransport::CloseSocket - "
"GetOverlappedResult err=%x\n", err));
if (err != ERROR_IO_PENDING) {
canClose = true;
}
}
} else {
SOCKET_LOG(("nsSocketTransport::CloseSocket - no overlapped struct\n"));
canClose = true;
}
} else {
SOCKET_LOG(("nsSocketTransport::CloseSocket - there is no "
"PR_FileDesc2PlatformOverlappedIOHandle function.\n"));
canClose = true;
}
if (canClose) {
PR_Close(aFd);
} else {
if (telemetryEnabled) {
Telemetry::ScalarAdd(Telemetry::ScalarID::NETWORK_TCP_OVERLAPPED_RESULT_DELAYED, 1);
}
aSTS->AddOverlappedPendingSocket(aFd);
}
#else
PR_Close(aFd);
#endif
if (telemetryEnabled) {
SendPRBlockingTelemetry(closeStarted,
Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL,
Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN,

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

@ -170,7 +170,7 @@ public:
uint64_t ByteCountReceived() override { return mInput.ByteCount(); }
uint64_t ByteCountSent() override { return mOutput.ByteCount(); }
static void CloseSocket(PRFileDesc *aFd, bool aTelemetryEnabled);
static void CloseSocket(PRFileDesc *aFd, nsSocketTransportService *aSTS);
static void SendPRBlockingTelemetry(PRIntervalTime aStart,
Telemetry::HistogramID aIDNormal,
Telemetry::HistogramID aIDShutdown,

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

@ -98,6 +98,11 @@ nsSocketTransportService::nsSocketTransportService()
#if defined(XP_WIN)
, mPolling(false)
#endif
#if defined(_WIN64) && defined(WIN95)
, mFileDesc2PlatformOverlappedIOHandleFuncChecked(false)
, mNsprLibrary(nullptr)
, mFileDesc2PlatformOverlappedIOHandleFunc(nullptr)
#endif
{
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
@ -122,6 +127,12 @@ nsSocketTransportService::~nsSocketTransportService()
free(mIdleList);
free(mPollList);
gSocketTransportService = nullptr;
#if defined(_WIN64) && defined(WIN95)
if (mNsprLibrary) {
BOOL rv = FreeLibrary(mNsprLibrary);
SOCKET_LOG(("Free nspr library: %d\n", rv));
}
#endif
}
//-----------------------------------------------------------------------------
@ -944,6 +955,9 @@ nsSocketTransportService::Run()
TimeStamp::NowLoRes());
pollDuration += singlePollDuration;
}
#if defined(_WIN64) && defined(WIN95)
CheckOverlappedPendingSocketsAreDone();
#endif
mRawThread->HasPendingEvents(&pendingEvents);
if (pendingEvents) {
@ -1036,6 +1050,19 @@ nsSocketTransportService::Run()
// socket detach handlers get processed.
NS_ProcessPendingEvents(mRawThread);
#if defined(_WIN64) && defined(WIN95)
// Final pass over panding overlapped ios. If they are not finish we will
// leak FDs.
CheckOverlappedPendingSocketsAreDone();
#ifdef NS_BUILD_REFCNT_LOGGING
if (mOverlappedPendingSockets.Length()) {
PRIntervalTime delay = PR_MillisecondsToInterval(5000);
PR_Sleep(delay); // wait another 5s.
CheckOverlappedPendingSocketsAreDone();
}
#endif
#endif
gSocketThread = nullptr;
psm::StopSSLServerCertVerificationThreads();
@ -1674,5 +1701,106 @@ nsSocketTransportService::EndPolling()
}
#endif
#if defined(_WIN64) && defined(WIN95)
void
nsSocketTransportService::AddOverlappedPendingSocket(PRFileDesc *aFd)
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
MOZ_ASSERT(HasFileDesc2PlatformOverlappedIOHandleFunc());
SOCKET_LOG(("STS AddOverlappedPendingSocket append aFd=%p\n", aFd));
mOverlappedPendingSockets.AppendElement(aFd);
}
void
nsSocketTransportService::CheckOverlappedPendingSocketsAreDone()
{
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
if (!HasFileDesc2PlatformOverlappedIOHandleFunc()) {
return;
}
SOCKET_LOG(("STS CheckOverlappedPendingSocketsAreDone: "
"pending sockets = %d\n", mOverlappedPendingSockets.Length()));
for (int i = mOverlappedPendingSockets.Length() - 1; i >= 0; i--) {
bool canClose = false;
PRFileDesc *fd = mOverlappedPendingSockets[i];
LPOVERLAPPED ol = nullptr;
if (mFileDesc2PlatformOverlappedIOHandleFunc(fd, (void**)&ol) == PR_SUCCESS) {
PROsfd osfd = PR_FileDesc2NativeHandle(fd);
SOCKET_LOG(("STS CheckOverlappedPendingSocketsAreDone "
"fd=%p osfd=%d\n", fd, (int)osfd));
DWORD rvSent;
if (GetOverlappedResult((HANDLE)osfd, ol, &rvSent, FALSE) == TRUE) {
SOCKET_LOG(("STS CheckOverlappedPendingSocketsAreDone "
"GetOverlappedResult succeeded\n"));
canClose = true;
} else {
int err = WSAGetLastError();
SOCKET_LOG(("STS CheckOverlappedPendingSocketsAreDone "
"GetOverlappedResult failed error=%x \n", err));
if (err != ERROR_IO_PENDING) {
canClose = true;
}
}
} else {
canClose = true;
}
if (canClose) {
SOCKET_LOG(("STS CheckOverlappedPendingSocketsAreDone "
"remove fd=%p\n", fd));
mOverlappedPendingSockets.RemoveElementAt(i);
PR_Close(fd);
}
}
SOCKET_LOG(("STS CheckOverlappedPendingSocketsAreDone: end a check, "
"pending sockets = %d\n", mOverlappedPendingSockets.Length()));
}
void
nsSocketTransportService::CheckFileDesc2PlatformOverlappedIOHandleFunc()
{
if (mFileDesc2PlatformOverlappedIOHandleFuncChecked) {
return;
}
mFileDesc2PlatformOverlappedIOHandleFuncChecked = true;
HMODULE library = LoadLibraryA("nss3.dll");
MOZ_ASSERT(library);
if (library) {
mFileDesc2PlatformOverlappedIOHandleFunc =
reinterpret_cast<FileDesc2PlatformOverlappedIOHandleFunc>(
GetProcAddress(library, "PR_FileDesc2PlatformOverlappedIOHandle"));
if (mFileDesc2PlatformOverlappedIOHandleFunc) {
SOCKET_LOG(("FileDesc2PlatformOverlappedIOHandle function "
"present"));
mNsprLibrary = library;
} else {
BOOL rv = FreeLibrary(library);
SOCKET_LOG(("No FileDesc2PlatformOverlappedIOHandle function: "
"%d\n", rv));
}
}
}
bool
nsSocketTransportService::HasFileDesc2PlatformOverlappedIOHandleFunc()
{
CheckFileDesc2PlatformOverlappedIOHandleFunc();
return mFileDesc2PlatformOverlappedIOHandleFunc;
}
PRStatus
nsSocketTransportService::CallFileDesc2PlatformOverlappedIOHandleFunc(PRFileDesc *fd, void **ol)
{
CheckFileDesc2PlatformOverlappedIOHandleFunc();
if (mFileDesc2PlatformOverlappedIOHandleFunc) {
return mFileDesc2PlatformOverlappedIOHandleFunc(fd, ol);
}
return PR_FAILURE;
}
#endif
} // namespace net
} // namespace mozilla

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

@ -24,6 +24,12 @@
#include "mozilla/UniquePtr.h"
#include "PollableEvent.h"
#if defined(_WIN64) && defined(WIN95)
#include "WinDef.h"
typedef PRStatus (*FileDesc2PlatformOverlappedIOHandleFunc)(PRFileDesc *fd, void **ol);
#endif
class nsASocketHandler;
struct PRPollDesc;
class nsIPrefBranch;
@ -120,6 +126,12 @@ public:
bool IsTelemetryEnabledAndNotSleepPhase() { return mTelemetryEnabledPref &&
!mSleepPhase; }
PRIntervalTime MaxTimeForPrClosePref() {return mMaxTimeForPrClosePref; }
#if defined(_WIN64) && defined(WIN95)
void AddOverlappedPendingSocket(PRFileDesc *aFd);
bool HasFileDesc2PlatformOverlappedIOHandleFunc();
PRStatus CallFileDesc2PlatformOverlappedIOHandleFunc(PRFileDesc *fd, void **ol);
#endif
protected:
virtual ~nsSocketTransportService();
@ -270,6 +282,21 @@ private:
void StartPolling();
void EndPolling();
#endif
#if defined(_WIN64) && defined(WIN95)
// If TCP Fast Open is used on Windows an overlapped io is used. We need to
// wait until this io is finished or canceled before detroying its
// PRFileDesc.
nsTArray<PRFileDesc *> mOverlappedPendingSockets;
bool mFileDesc2PlatformOverlappedIOHandleFuncChecked;
HMODULE mNsprLibrary;
FileDesc2PlatformOverlappedIOHandleFunc mFileDesc2PlatformOverlappedIOHandleFunc;
void CheckFileDesc2PlatformOverlappedIOHandleFunc();
void CheckOverlappedPendingSocketsAreDone();
#endif
};
extern nsSocketTransportService *gSocketTransportService;

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

@ -3587,7 +3587,12 @@ nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *specificCI
if (!specificEnt) {
RefPtr<nsHttpConnectionInfo> clone(specificCI->Clone());
specificEnt = new nsConnectionEntry(clone);
#if defined(_WIN64) && defined(WIN95)
specificEnt->mUseFastOpen = gHttpHandler->UseFastOpen() &&
gSocketTransportService->HasFileDesc2PlatformOverlappedIOHandleFunc();
#else
specificEnt->mUseFastOpen = gHttpHandler->UseFastOpen();
#endif
mCT.Put(clone->HashKey(), specificEnt);
}
return specificEnt;

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

@ -813,6 +813,38 @@ formautofill.addresses:
record_in_processes:
- 'main'
# The following section is for probes for tcp protocol.
network.tcp:
overlapped_io_canceled_before_finished:
bug_numbers:
- 1363372
description: >
Count the number of sockets that use overlapped io and are canceled before
the overlapped io finishes. (We only use overlapped io for connectEx when
TCP Fast Open is used.)
expires: "58"
kind: uint
notification_emails:
- necko@mozilla.com
- ddamjanovic@ozilla.com
record_in_processes:
- 'main'
overlapped_result_delayed:
bug_numbers:
- 1363372
description: >
Count the number of overlapped io that are canceled but cancelation
have not completed immediately. (We only use overlapped io for
connectEx when TCP Fast Open is used.)
expires: "58"
kind: uint
notification_emails:
- necko@mozilla.com
- ddamjanovic@mozilla.com
record_in_processes:
- 'main'
# The following section is for probes testing the Telemetry system. They will not be
# submitted in pings and are only used for testing.
telemetry.test: