bug #7428. Added support for connection timeout. Now if a connection cannot be made within 35 seconds, the transport times out.

This commit is contained in:
rpotts%netscape.com 1999-11-06 01:32:34 +00:00
parent 7a5278d0a4
commit 7d63cbc945
5 changed files with 189 additions and 72 deletions

View File

@ -37,7 +37,7 @@
#define NS_ERROR_CONNECTION_REFUSED \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 13)
#define NS_ERROR_TCP_TIMEOUT \
#define NS_ERROR_NET_TIMEOUT \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 14)
#define NS_ERROR_IN_PROGRESS \

View File

@ -83,14 +83,16 @@ nsSocketState gStateTable[eSocketOperation_Max][eSocketState_Max] = {
//
// This is the timeout value (in milliseconds) for calls to PR_Connect(...).
// The socket transport thread will block for this amount of time waiting
// for the PR_Connect(...) to complete...
//
// The gConnectTimeout gets initialized the first time a nsSocketTransport
// is created... This interval is then passed to all PR_Connect() calls...
//
#define CONNECT_TIMEOUT_IN_MS 20
static int gTimeoutIsInitialized = 0;
static PRIntervalTime gConnectTimeout = PR_INTERVAL_NO_TIMEOUT;
static PRIntervalTime gConnectTimeout = PR_INTERVAL_NO_WAIT;
static PRIntervalTime gTimeoutInterval = PR_INTERVAL_NO_WAIT;
//
// This is the global buffer used by all nsSocketTransport instances when
@ -138,6 +140,8 @@ nsSocketTransport::nsSocketTransport():
PR_INIT_CLIST(&mListLink);
mLastActiveTime = PR_INTERVAL_NO_WAIT;
SetReadType (eSocketRead_None);
SetWriteType(eSocketWrite_None);
@ -150,9 +154,8 @@ nsSocketTransport::nsSocketTransport():
//
// Initialize the global connect timeout value if necessary...
//
if (0 == gTimeoutIsInitialized) {
gConnectTimeout = PR_MillisecondsToInterval(CONNECT_TIMEOUT_IN_MS);
gTimeoutIsInitialized = 1;
if (PR_INTERVAL_NO_WAIT == gConnectTimeout) {
gConnectTimeout = PR_MillisecondsToInterval(CONNECT_TIMEOUT_IN_MS);
}
#if defined(PR_LOGGING)
@ -290,6 +293,9 @@ nsresult nsSocketTransport::Init(nsSocketTransportService* aService,
}
}
// Update the active time for timeout purposes...
mLastActiveTime = PR_IntervalNow();
PR_LOG(gSocketLog, PR_LOG_DEBUG,
("Initializing nsSocketTransport [this=%x]. rv = %x. \t"
"aHost = %s.\t"
@ -300,6 +306,35 @@ nsresult nsSocketTransport::Init(nsSocketTransportService* aService,
}
nsresult nsSocketTransport::CheckForTimeout(PRIntervalTime aCurrentTime)
{
nsresult rv = NS_OK;
PRIntervalTime idleInterval;
// Enter the socket transport lock...
nsAutoLock aLock(mLock);
idleInterval = aCurrentTime - mLastActiveTime;
//
// Only timeout if the transport is waiting to connect to the server
//
if ((eSocketState_WaitConnect == mCurrentState) &&
(idleInterval >= gTimeoutInterval)) {
PR_LOG(gSocketLog, PR_LOG_ERROR,
("nsSocketTransport::CheckForTimeout() [this=%x].\t"
"TIMED OUT... Idle interval: %d\n",
this, idleInterval));
// Move the transport into the Timeout state...
mCurrentState = eSocketState_Timeout;
rv = NS_ERROR_NET_TIMEOUT;
}
return rv;
}
nsresult nsSocketTransport::Process(PRInt16 aSelectFlags)
{
PRBool done = PR_FALSE;
@ -420,6 +455,10 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags)
mReadListener = null_nsCOMPtr();
mReadContext = null_nsCOMPtr();
}
// Close the socket transport end of the pipe...
if (mReadPipeOut) {
mReadPipeOut->Close();
}
mReadPipeIn = null_nsCOMPtr();
mReadPipeOut = null_nsCOMPtr();
SetReadType(eSocketRead_None);
@ -438,6 +477,10 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags)
mWriteObserver = null_nsCOMPtr();
mWriteContext = null_nsCOMPtr();
}
// Close the socket transport end of the pipe...
if (mWritePipeIn) {
mWritePipeIn->Close();
}
mWritePipeIn = null_nsCOMPtr();
mWritePipeOut = null_nsCOMPtr();
SetWriteType(eSocketWrite_None);
@ -492,8 +535,7 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags)
break;
case eSocketState_Timeout:
NS_ASSERTION(0, "Unexpected state...");
mStatus = NS_ERROR_FAILURE;
mStatus = NS_ERROR_NET_TIMEOUT;
break;
default:
@ -520,6 +562,9 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags)
aSelectFlags = 0;
}
// Update the active time for timeout purposes...
mLastActiveTime = PR_IntervalNow();
PR_LOG(gSocketLog, PR_LOG_DEBUG,
("--- Leaving nsSocketTransport::Process() [this=%x]. mStatus = %x.\t"
"CurrentState = %d\n\n",
@ -1084,6 +1129,7 @@ nsresult nsSocketTransport::doWriteFromStream(PRUint32 *aCount)
return rv;
}
nsresult nsSocketTransport::CloseConnection(PRBool bNow)
{
PRStatus status;
@ -1121,6 +1167,12 @@ nsresult nsSocketTransport::CloseConnection(PRBool bNow)
}
void nsSocketTransport::SetSocketTimeout(PRIntervalTime aTimeInterval)
{
gTimeoutInterval = aTimeInterval;
}
//
// --------------------------------------------------------------------------
// nsISupports implementation...

View File

@ -22,6 +22,7 @@
#include "prclist.h"
#include "prio.h"
#include "prnetdb.h"
#include "prinrval.h"
#include "nsCOMPtr.h"
#include "nsIChannel.h"
@ -132,6 +133,8 @@ public:
nsresult Process(PRInt16 aSelectFlags);
nsresult CheckForTimeout(PRIntervalTime aCurrentTime);
// Close this socket either right away or once done with the transaction.
nsresult CloseConnection(PRBool bNow=PR_TRUE);
@ -142,8 +145,11 @@ public:
static nsSocketTransport* GetInstance(PRCList* qp) { return (nsSocketTransport*)((char*)qp - offsetof(nsSocketTransport, mListLink)); }
static void SetSocketTimeout(PRIntervalTime aTimeoutInterval);
PRBool CanBeReused(void) { return
(mCurrentState != eSocketState_Error) && !mCloseConnectionOnceDone;}
protected:
nsresult doConnection(PRInt16 aSelectFlags);
nsresult doResolveHost(void);
@ -189,6 +195,7 @@ protected:
nsCOMPtr<nsIRequest> mDNSRequest;
nsCOMPtr<nsIProgressEventSink> mEventSink;
char* mHostName;
PRIntervalTime mLastActiveTime;
PRCList mListLink;
PRUint32 mLoadAttributes;
PRLock* mLock;

View File

@ -18,12 +18,11 @@
*/
#include "nsILoadGroup.h"
#include "netCore.h"
#include "nsSocketTransportService.h"
#include "nsSocketTransport.h"
#include "nsAutoLock.h"
#define MAX_OPEN_CONNECTIONS 50
nsSocketTransportService::nsSocketTransportService()
{
@ -43,6 +42,8 @@ nsSocketTransportService::nsSocketTransportService()
mActiveTransportList = nsnull;
mThreadRunning = PR_FALSE;
SetSocketTimeoutInterval(PR_MillisecondsToInterval(DEFAULT_SOCKET_TIMEOUT_IN_MS));
}
@ -332,6 +333,23 @@ nsresult nsSocketTransportService::RemoveFromSelectList(nsSocketTransport* aTran
}
nsresult
nsSocketTransportService::GetSocketTimeoutInterval(PRIntervalTime* aResult)
{
*aResult = mSocketTimeoutInterval;
return NS_OK;
}
nsresult
nsSocketTransportService::SetSocketTimeoutInterval(PRIntervalTime aTime)
{
mSocketTimeoutInterval = aTime;
// Update the timeout value in the socket transport...
nsSocketTransport::SetSocketTimeout(aTime);
return NS_OK;
}
//
// --------------------------------------------------------------------------
@ -370,8 +388,7 @@ nsSocketTransportService::QueryInterface(const nsIID& aIID, void* *aInstancePtr)
NS_IMETHODIMP
nsSocketTransportService::Run(void)
{
PRIntervalTime pollTimeout
;
PRIntervalTime pollTimeout;
#ifdef USE_POLLABLE_EVENT
//
// Initialize the FDSET used by PR_Poll(...). The first item in the FDSet
@ -380,7 +397,7 @@ nsSocketTransportService::Run(void)
mSelectFDSet[0].fd = mThreadEvent;
mSelectFDSet[0].in_flags = PR_POLL_READ;
mSelectFDSetCount = 1;
pollTimeout = PR_INTERVAL_NO_TIMEOUT;
pollTimeout = mSocketTimeoutInterval;
#else
//
// For now, rather than breaking out of the call to PR_Poll(...) just set
@ -396,73 +413,95 @@ nsSocketTransportService::Run(void)
while (mThreadRunning) {
nsresult rv;
PRInt32 count;
PRIntervalTime intervalNow;
nsSocketTransport* transport;
int i;
// XXX: PR_Poll(...) needs a timeout value...
count = PR_Poll(mSelectFDSet, mSelectFDSetCount, pollTimeout);
if (-1 == count) {
// XXX: PR_Poll failed... What should happen?
}
/* One or more sockets has data... */
if (count > 0) {
int i;
intervalNow = PR_IntervalNow();
//
// See if any sockets have data...
//
// Walk the list of active transports backwards to avoid missing
// elements when a transport is removed...
//
for (i=mSelectFDSetCount-1; i>=0; i--) {
PRPollDesc* pfd;
PRInt16 out_flags;
transport = mActiveTransportList[i];
pfd = &mSelectFDSet[i];
/* Process any sockets with data first... */
for (i=mSelectFDSetCount-1; i>=0; i--) {
PRPollDesc* pfd;
PRInt16 out_flags;
//
// XXX: PR_Poll(...) has the unpleasent behavior of ONLY setting the
// out_flags if one or more FDs are ready. So, DO NOT look at
// the out_flags unless count > 0.
//
if ((count > 0) && pfd->out_flags) {
// Clear the out_flags for next time...
out_flags = pfd->out_flags;
pfd->out_flags = 0;
pfd = &mSelectFDSet[i];
if (pfd->out_flags) {
// Clear the out_flags for next time...
out_flags = pfd->out_flags;
pfd->out_flags = 0;
transport = mActiveTransportList[i];
if (transport) {
rv = transport->Process(out_flags);
if (NS_BASE_STREAM_WOULD_BLOCK == rv) {
// Update the select flags...
pfd->in_flags = transport->GetSelectFlags();
}
//
// If the operation completed, then remove the entry from the
// select list...
//
else {
rv = RemoveFromSelectList(transport);
}
if (transport) {
rv = transport->Process(out_flags);
if (NS_BASE_STREAM_WOULD_BLOCK == rv) {
// Update the select flags...
pfd->in_flags = transport->GetSelectFlags();
}
//
// If the operation completed, then remove the entry from the
// select list...
//
else {
rv = RemoveFromSelectList(transport);
}
}
else {
#ifdef USE_POLLABLE_EVENT
/* Process any pending operations on the mWorkQ... */
NS_ASSERTION(0 == i, "Null transport in active list...");
if (0 == i) {
//
// Clear the pollable event... This call should *never* block since
// PR_Poll(...) said that it had been fired...
//
NS_ASSERTION(!(mSelectFDSet[0].out_flags & PR_POLL_EXCEPT),
"Exception on Pollable event.");
PR_WaitForPollableEvent(mThreadEvent);
/* Process any pending operations on the mWorkQ... */
NS_ASSERTION(0 == i, "Null transport in active list...");
if (0 == i) {
//
// Clear the pollable event... This call should *never* block since
// PR_Poll(...) said that it had been fired...
//
NS_ASSERTION(!(mSelectFDSet[0].out_flags & PR_POLL_EXCEPT),
"Exception on Pollable event.");
PR_WaitForPollableEvent(mThreadEvent);
rv = ProcessWorkQ();
}
rv = ProcessWorkQ();
}
#else
//
// The pollable event should be the *only* null transport
// in the active transport list.
//
NS_ASSERTION(transport, "Null transport in active list...");
//
// The pollable event should be the *only* null transport
// in the active transport list.
//
NS_ASSERTION(transport, "Null transport in active list...");
#endif /* USE_POLLABLE_EVENT */
}
//
// Check to see if the transport has timed out...
//
} else {
if (transport) {
rv = transport->CheckForTimeout(intervalNow);
if (NS_ERROR_NET_TIMEOUT == rv) {
// Process the timeout...
rv = transport->Process(0);
//
// The operation has completed. Remove the entry from the
// select list///
//
rv = RemoveFromSelectList(transport);
}
}
}
}
/* PR_Poll(...) timeout... */
else if (count == 0) {
}
/* PR_Poll(...) error.. */
else {
}
} // end-for
#ifndef USE_POLLABLE_EVENT
/* Process any pending operations on the mWorkQ... */

View File

@ -34,6 +34,19 @@
#define USE_POLLABLE_EVENT
#endif
//
// This is the default timeout value (in milliseconds) for sockets which have
// no activity...
//
#define DEFAULT_SOCKET_TIMEOUT_IN_MS 35*1000
//
// This is the Maximum number of Socket Transport instances that can be active
// at once...
//
#define MAX_OPEN_CONNECTIONS 50
// Forward declarations...
class nsSocketTransport;
@ -56,6 +69,10 @@ public:
nsresult AddToWorkQ(nsSocketTransport* aTransport);
// XXX: Should these use intervals or Milliseconds?
nsresult GetSocketTimeoutInterval(PRIntervalTime* aResult);
nsresult SetSocketTimeoutInterval(PRIntervalTime aTime);
// The following methods are called by the transport thread...
nsresult ProcessWorkQ(void);
@ -63,18 +80,20 @@ public:
nsresult RemoveFromSelectList(nsSocketTransport* aTransport);
protected:
nsIThread* mThread;
nsIThread* mThread;
#ifdef USE_POLLABLE_EVENT
PRFileDesc* mThreadEvent;
PRFileDesc* mThreadEvent;
#endif /* USE_POLLABLE_EVENT */
PRLock* mThreadLock;
PRBool mThreadRunning;
PRLock* mThreadLock;
PRBool mThreadRunning;
PRCList mWorkQ;
PRIntervalTime mSocketTimeoutInterval;
PRInt32 mSelectFDSetCount;
PRPollDesc* mSelectFDSet;
nsSocketTransport** mActiveTransportList;
PRCList mWorkQ;
PRInt32 mSelectFDSetCount;
PRPollDesc* mSelectFDSet;
nsSocketTransport** mActiveTransportList;
};