зеркало из https://github.com/mozilla/pjs.git
fixes bug 278144 "Support socket i/o timeouts" r=bienvenu sr=bzbarsky
This commit is contained in:
Родитель
d5febfb9de
Коммит
f27e88999a
|
@ -48,7 +48,7 @@ native PRNetAddr(union PRNetAddr);
|
|||
* NOTE: This is a free-threaded interface, meaning that the methods on
|
||||
* this interface may be called from any thread.
|
||||
*/
|
||||
[scriptable, uuid(ee783990-c87c-4ace-87ca-54e5fcc477b0)]
|
||||
[scriptable, uuid(66418cc8-5f5d-4f52-a7f9-db8fb3b2cfe6)]
|
||||
interface nsISocketTransport : nsITransport
|
||||
{
|
||||
/**
|
||||
|
@ -93,6 +93,18 @@ interface nsISocketTransport : nsITransport
|
|||
*/
|
||||
boolean isAlive();
|
||||
|
||||
/**
|
||||
* Socket timeouts in seconds.
|
||||
*/
|
||||
unsigned long getTimeout(in unsigned long aType);
|
||||
void setTimeout(in unsigned long aType, in unsigned long aValue);
|
||||
|
||||
/**
|
||||
* Values for the aType parameter passed to get/setTimeout.
|
||||
*/
|
||||
const unsigned long TIMEOUT_CONNECT = 0;
|
||||
const unsigned long TIMEOUT_READ_WRITE = 1;
|
||||
|
||||
/**
|
||||
* nsITransportEventSink status codes.
|
||||
*
|
||||
|
|
|
@ -104,9 +104,6 @@ nsServerSocket::nsServerSocket()
|
|||
, mFD(nsnull)
|
||||
, mAttached(PR_FALSE)
|
||||
{
|
||||
mCondition = NS_OK;
|
||||
mPollFlags = 0;
|
||||
|
||||
// we want to be able to access the STS directly, and it may not have been
|
||||
// constructed yet. the STS constructor sets gSocketTransportService.
|
||||
if (!gSocketTransportService)
|
||||
|
@ -223,6 +220,11 @@ nsServerSocket::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
|
|||
NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
|
||||
NS_ASSERTION(mFD == fd, "wrong file descriptor");
|
||||
|
||||
// If our poll timeout (of 2^16 seconds!) is reached, just go back to
|
||||
// polling on the listening socket.
|
||||
if (outFlags == -1)
|
||||
return;
|
||||
|
||||
if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL))
|
||||
{
|
||||
NS_WARNING("error polling on listening socket");
|
||||
|
|
|
@ -72,6 +72,14 @@
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Large default timeouts approximate behavior of no timeout. (It's better to
|
||||
// let the servers or host operating system time us out.) These timeout values
|
||||
// are given in seconds.
|
||||
#define DEFAULT_TIMEOUT_CONNECT (10 * 60)
|
||||
#define DEFAULT_TIMEOUT_READ_WRITE (10 * 60)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
|
||||
static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
|
||||
|
||||
|
@ -695,6 +703,9 @@ nsSocketTransport::nsSocketTransport()
|
|||
LOG(("creating nsSocketTransport @%x\n", this));
|
||||
|
||||
NS_ADDREF(gSocketTransportService);
|
||||
|
||||
mTimeouts[TIMEOUT_CONNECT] = DEFAULT_TIMEOUT_CONNECT;
|
||||
mTimeouts[TIMEOUT_READ_WRITE] = DEFAULT_TIMEOUT_READ_WRITE;
|
||||
}
|
||||
|
||||
nsSocketTransport::~nsSocketTransport()
|
||||
|
@ -806,6 +817,7 @@ nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const PRNetAddr *addr
|
|||
memcpy(&mNetAddr, addr, sizeof(PRNetAddr));
|
||||
|
||||
mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
|
||||
mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
|
||||
mState = STATE_TRANSFERRING;
|
||||
|
||||
mFD = fd;
|
||||
|
@ -1091,6 +1103,7 @@ nsSocketTransport::InitiateSocket()
|
|||
|
||||
LOG((" advancing to STATE_CONNECTING\n"));
|
||||
mState = STATE_CONNECTING;
|
||||
mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
|
||||
SendStatus(STATUS_CONNECTING_TO);
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
|
@ -1278,6 +1291,7 @@ nsSocketTransport::OnSocketConnected()
|
|||
LOG((" advancing to STATE_TRANSFERRING\n"));
|
||||
|
||||
mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
|
||||
mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
|
||||
mState = STATE_TRANSFERRING;
|
||||
|
||||
SendStatus(STATUS_CONNECTED_TO);
|
||||
|
@ -1415,6 +1429,12 @@ nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
|
|||
LOG(("nsSocketTransport::OnSocketReady [this=%x outFlags=%hd]\n",
|
||||
this, outFlags));
|
||||
|
||||
if (outFlags == -1) {
|
||||
LOG(("socket timeout expired\n"));
|
||||
mCondition = NS_ERROR_NET_TIMEOUT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState == STATE_TRANSFERRING) {
|
||||
// if waiting to write and socket is writable or hit an exception.
|
||||
if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
|
||||
|
@ -1430,6 +1450,8 @@ nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
|
|||
mPollFlags &= ~PR_POLL_READ;
|
||||
mInput.OnSocketReady(NS_OK);
|
||||
}
|
||||
// Update poll timeout in case it was changed
|
||||
mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
|
||||
}
|
||||
else if (mState == STATE_CONNECTING) {
|
||||
PRStatus status = PR_ConnectContinue(fd, outFlags);
|
||||
|
@ -1450,6 +1472,8 @@ nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
|
|||
if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
|
||||
// Set up the select flags for connect...
|
||||
mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
|
||||
// Update poll timeout in case it was changed
|
||||
mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
|
||||
}
|
||||
else {
|
||||
//
|
||||
|
@ -1758,6 +1782,23 @@ nsSocketTransport::GetSelfAddr(PRNetAddr *addr)
|
|||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::GetTimeout(PRUint32 type, PRUint32 *value)
|
||||
{
|
||||
NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
|
||||
*value = (PRUint32) mTimeouts[type];
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::SetTimeout(PRUint32 type, PRUint32 value)
|
||||
{
|
||||
NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
|
||||
// truncate overly large timeout values.
|
||||
mTimeouts[type] = (PRUint16) PR_MIN(value, PR_UINT16_MAX);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::OnLookupComplete(nsIDNSRequest *request,
|
||||
nsIDNSRecord *rec,
|
||||
|
|
|
@ -262,6 +262,9 @@ private:
|
|||
friend class nsSocketInputStream;
|
||||
friend class nsSocketOutputStream;
|
||||
|
||||
// socket timeouts are not protected by any lock.
|
||||
PRUint16 mTimeouts[2];
|
||||
|
||||
//
|
||||
// mFD access methods: called with mLock held.
|
||||
//
|
||||
|
|
|
@ -167,6 +167,7 @@ nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler
|
|||
SocketContext sock;
|
||||
sock.mFD = fd;
|
||||
sock.mHandler = handler;
|
||||
sock.mElapsedTime = 0;
|
||||
|
||||
nsresult rv = AddToIdleList(&sock);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
|
@ -299,8 +300,27 @@ nsSocketTransportService::MoveToPollList(SocketContext *sock)
|
|||
RemoveFromIdleList(sock);
|
||||
}
|
||||
|
||||
PRIntervalTime
|
||||
nsSocketTransportService::PollTimeout()
|
||||
{
|
||||
if (mActiveCount == 0)
|
||||
return NS_SOCKET_POLL_TIMEOUT;
|
||||
|
||||
// compute minimum time before any socket timeout expires.
|
||||
PRUint32 minR = PR_UINT16_MAX;
|
||||
for (PRUint32 i=0; i<mActiveCount; ++i) {
|
||||
const SocketContext &s = mActiveList[i];
|
||||
PRUint32 r = s.mHandler->mPollTimeout - s.mElapsedTime;
|
||||
NS_ASSERTION(r <= s.mHandler->mPollTimeout, "oops");
|
||||
if (r < minR)
|
||||
minR = r;
|
||||
}
|
||||
LOG(("poll timeout: %lu\n", minR));
|
||||
return PR_SecondsToInterval(minR);
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsSocketTransportService::Poll()
|
||||
nsSocketTransportService::Poll(PRUint32 *interval)
|
||||
{
|
||||
PRPollDesc *pollList;
|
||||
PRUint32 pollCount;
|
||||
|
@ -310,7 +330,7 @@ nsSocketTransportService::Poll()
|
|||
mPollList[0].out_flags = 0;
|
||||
pollList = mPollList;
|
||||
pollCount = mActiveCount + 1;
|
||||
pollTimeout = NS_SOCKET_POLL_TIMEOUT;
|
||||
pollTimeout = PollTimeout();
|
||||
}
|
||||
else {
|
||||
// no pollable event, so busy wait...
|
||||
|
@ -322,7 +342,12 @@ nsSocketTransportService::Poll()
|
|||
pollTimeout = PR_MillisecondsToInterval(25);
|
||||
}
|
||||
|
||||
return PR_Poll(pollList, pollCount, pollTimeout);
|
||||
PRIntervalTime ts = PR_IntervalNow();
|
||||
|
||||
PRInt32 rv = PR_Poll(pollList, pollCount, pollTimeout);
|
||||
|
||||
*interval = PR_IntervalToSeconds(PR_IntervalNow() - ts);
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRBool
|
||||
|
@ -524,20 +549,36 @@ nsSocketTransportService::Run()
|
|||
|
||||
LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
|
||||
|
||||
PRInt32 n = Poll();
|
||||
// Measures seconds spent while blocked on PR_Poll
|
||||
PRUint32 pollInterval;
|
||||
|
||||
PRInt32 n = Poll(&pollInterval);
|
||||
if (n < 0) {
|
||||
LOG((" PR_Poll error [%d]\n", PR_GetError()));
|
||||
active = PR_FALSE;
|
||||
}
|
||||
else if (n > 0) {
|
||||
else {
|
||||
//
|
||||
// service "active" sockets...
|
||||
//
|
||||
for (i=0; i<PRInt32(mActiveCount); ++i) {
|
||||
PRPollDesc &desc = mPollList[i+1];
|
||||
if (desc.out_flags != 0) {
|
||||
nsASocketHandler *handler = mActiveList[i].mHandler;
|
||||
handler->OnSocketReady(desc.fd, desc.out_flags);
|
||||
SocketContext &s = mActiveList[i];
|
||||
if (n > 0 && desc.out_flags != 0) {
|
||||
s.mElapsedTime = 0;
|
||||
s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
|
||||
}
|
||||
else {
|
||||
// update elapsed time counter
|
||||
if (NS_UNLIKELY(pollInterval > (PR_UINT16_MAX - s.mElapsedTime)))
|
||||
s.mElapsedTime = PR_UINT16_MAX;
|
||||
else
|
||||
s.mElapsedTime += PRUint16(pollInterval);
|
||||
// check for timeout expiration
|
||||
if (s.mElapsedTime >= s.mHandler->mPollTimeout) {
|
||||
s.mElapsedTime = 0;
|
||||
s.mHandler->OnSocketReady(desc.fd, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -553,19 +594,14 @@ nsSocketTransportService::Run()
|
|||
//
|
||||
// service the event queue (mPollList[0].fd == mThreadEvent)
|
||||
//
|
||||
if (mPollList[0].out_flags == PR_POLL_READ) {
|
||||
if (n == 0)
|
||||
active = ServiceEventQ();
|
||||
else if (mPollList[0].out_flags == PR_POLL_READ) {
|
||||
// acknowledge pollable event (wait should not block)
|
||||
PR_WaitForPollableEvent(mThreadEvent);
|
||||
active = ServiceEventQ();
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG((" PR_Poll timed out\n"));
|
||||
//
|
||||
// service event queue whenever PR_Poll times out.
|
||||
//
|
||||
active = ServiceEventQ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -70,7 +70,11 @@ extern PRLogModuleInfo *gSocketTransportLog;
|
|||
class nsASocketHandler : public nsISupports
|
||||
{
|
||||
public:
|
||||
nsASocketHandler() : mCondition(NS_OK), mPollFlags(0) {}
|
||||
nsASocketHandler()
|
||||
: mCondition(NS_OK)
|
||||
, mPollFlags(0)
|
||||
, mPollTimeout(PR_UINT16_MAX)
|
||||
{}
|
||||
|
||||
//
|
||||
// this condition variable will be checked to determine if the socket
|
||||
|
@ -86,6 +90,13 @@ public:
|
|||
//
|
||||
PRUint16 mPollFlags;
|
||||
|
||||
//
|
||||
// this value specifies the maximum amount of time in seconds that may be
|
||||
// spent waiting for activity on this socket. if this timeout is reached,
|
||||
// then OnSocketReady will be called with outFlags = -1.
|
||||
//
|
||||
PRUint16 mPollTimeout;
|
||||
|
||||
//
|
||||
// called to service a socket
|
||||
//
|
||||
|
@ -93,6 +104,7 @@ public:
|
|||
// socketRef - socket identifier
|
||||
// fd - socket file descriptor
|
||||
// outFlags - value of PR_PollDesc::out_flags after PR_Poll returns
|
||||
// or -1 if a timeout occured
|
||||
//
|
||||
virtual void OnSocketReady(PRFileDesc *fd, PRInt16 outFlags) = 0;
|
||||
|
||||
|
@ -185,6 +197,7 @@ private:
|
|||
{
|
||||
PRFileDesc *mFD;
|
||||
nsASocketHandler *mHandler;
|
||||
PRUint16 mElapsedTime; // time elapsed w/o activity
|
||||
};
|
||||
|
||||
SocketContext mActiveList [ NS_SOCKET_MAX_COUNT ];
|
||||
|
@ -213,7 +226,10 @@ private:
|
|||
|
||||
PRPollDesc mPollList[ NS_SOCKET_MAX_COUNT + 1 ];
|
||||
|
||||
PRInt32 Poll(); // calls PR_Poll
|
||||
PRIntervalTime PollTimeout(); // computes ideal poll timeout
|
||||
PRInt32 Poll(PRUint32 *interval); // calls PR_Poll. the out param
|
||||
// interval indicates the poll
|
||||
// duration in seconds.
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// pending socket queue - see NotifyWhenCanAttachSocket
|
||||
|
|
Загрузка…
Ссылка в новой задаче