fixes bug 278144 "Support socket i/o timeouts" r=bienvenu sr=bzbarsky

This commit is contained in:
darin%meer.net 2005-01-26 02:13:14 +00:00
Родитель d5febfb9de
Коммит f27e88999a
6 изменённых файлов: 132 добавлений и 22 удалений

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

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