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
Родитель 6ffdc40813
Коммит aaff8f1fe9
5 изменённых файлов: 189 добавлений и 72 удалений

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

@ -37,7 +37,7 @@
#define NS_ERROR_CONNECTION_REFUSED \ #define NS_ERROR_CONNECTION_REFUSED \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 13) 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) NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 14)
#define NS_ERROR_IN_PROGRESS \ #define NS_ERROR_IN_PROGRESS \

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

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

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

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

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

@ -18,12 +18,11 @@
*/ */
#include "nsILoadGroup.h" #include "nsILoadGroup.h"
#include "netCore.h"
#include "nsSocketTransportService.h" #include "nsSocketTransportService.h"
#include "nsSocketTransport.h" #include "nsSocketTransport.h"
#include "nsAutoLock.h" #include "nsAutoLock.h"
#define MAX_OPEN_CONNECTIONS 50
nsSocketTransportService::nsSocketTransportService() nsSocketTransportService::nsSocketTransportService()
{ {
@ -43,6 +42,8 @@ nsSocketTransportService::nsSocketTransportService()
mActiveTransportList = nsnull; mActiveTransportList = nsnull;
mThreadRunning = PR_FALSE; 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 NS_IMETHODIMP
nsSocketTransportService::Run(void) nsSocketTransportService::Run(void)
{ {
PRIntervalTime pollTimeout PRIntervalTime pollTimeout;
;
#ifdef USE_POLLABLE_EVENT #ifdef USE_POLLABLE_EVENT
// //
// Initialize the FDSET used by PR_Poll(...). The first item in the FDSet // 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].fd = mThreadEvent;
mSelectFDSet[0].in_flags = PR_POLL_READ; mSelectFDSet[0].in_flags = PR_POLL_READ;
mSelectFDSetCount = 1; mSelectFDSetCount = 1;
pollTimeout = PR_INTERVAL_NO_TIMEOUT; pollTimeout = mSocketTimeoutInterval;
#else #else
// //
// For now, rather than breaking out of the call to PR_Poll(...) just set // For now, rather than breaking out of the call to PR_Poll(...) just set
@ -396,73 +413,95 @@ nsSocketTransportService::Run(void)
while (mThreadRunning) { while (mThreadRunning) {
nsresult rv; nsresult rv;
PRInt32 count; PRInt32 count;
PRIntervalTime intervalNow;
nsSocketTransport* transport; nsSocketTransport* transport;
int i;
// XXX: PR_Poll(...) needs a timeout value...
count = PR_Poll(mSelectFDSet, mSelectFDSetCount, pollTimeout); count = PR_Poll(mSelectFDSet, mSelectFDSetCount, pollTimeout);
if (-1 == count) {
// XXX: PR_Poll failed... What should happen?
}
/* One or more sockets has data... */ intervalNow = PR_IntervalNow();
if (count > 0) { //
int i; // 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... */ /* Process any sockets with data first... */
for (i=mSelectFDSetCount-1; i>=0; i--) { //
PRPollDesc* pfd; // XXX: PR_Poll(...) has the unpleasent behavior of ONLY setting the
PRInt16 out_flags; // 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 (transport) {
if (pfd->out_flags) { rv = transport->Process(out_flags);
// Clear the out_flags for next time... if (NS_BASE_STREAM_WOULD_BLOCK == rv) {
out_flags = pfd->out_flags; // Update the select flags...
pfd->out_flags = 0; pfd->in_flags = transport->GetSelectFlags();
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 the operation completed, then remove the entry from the
// select list...
//
else { else {
rv = RemoveFromSelectList(transport);
}
}
else {
#ifdef USE_POLLABLE_EVENT #ifdef USE_POLLABLE_EVENT
/* Process any pending operations on the mWorkQ... */ /* Process any pending operations on the mWorkQ... */
NS_ASSERTION(0 == i, "Null transport in active list..."); NS_ASSERTION(0 == i, "Null transport in active list...");
if (0 == i) { if (0 == i) {
// //
// Clear the pollable event... This call should *never* block since // Clear the pollable event... This call should *never* block since
// PR_Poll(...) said that it had been fired... // PR_Poll(...) said that it had been fired...
// //
NS_ASSERTION(!(mSelectFDSet[0].out_flags & PR_POLL_EXCEPT), NS_ASSERTION(!(mSelectFDSet[0].out_flags & PR_POLL_EXCEPT),
"Exception on Pollable event."); "Exception on Pollable event.");
PR_WaitForPollableEvent(mThreadEvent); PR_WaitForPollableEvent(mThreadEvent);
rv = ProcessWorkQ(); rv = ProcessWorkQ();
} }
#else #else
// //
// The pollable event should be the *only* null transport // The pollable event should be the *only* null transport
// in the active transport list. // in the active transport list.
// //
NS_ASSERTION(transport, "Null transport in active list..."); NS_ASSERTION(transport, "Null transport in active list...");
#endif /* USE_POLLABLE_EVENT */ #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);
} }
} }
} }
} } // end-for
/* PR_Poll(...) timeout... */
else if (count == 0) {
}
/* PR_Poll(...) error.. */
else {
}
#ifndef USE_POLLABLE_EVENT #ifndef USE_POLLABLE_EVENT
/* Process any pending operations on the mWorkQ... */ /* Process any pending operations on the mWorkQ... */

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

@ -34,6 +34,19 @@
#define USE_POLLABLE_EVENT #define USE_POLLABLE_EVENT
#endif #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... // Forward declarations...
class nsSocketTransport; class nsSocketTransport;
@ -56,6 +69,10 @@ public:
nsresult AddToWorkQ(nsSocketTransport* aTransport); 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... // The following methods are called by the transport thread...
nsresult ProcessWorkQ(void); nsresult ProcessWorkQ(void);
@ -63,18 +80,20 @@ public:
nsresult RemoveFromSelectList(nsSocketTransport* aTransport); nsresult RemoveFromSelectList(nsSocketTransport* aTransport);
protected: protected:
nsIThread* mThread; nsIThread* mThread;
#ifdef USE_POLLABLE_EVENT #ifdef USE_POLLABLE_EVENT
PRFileDesc* mThreadEvent; PRFileDesc* mThreadEvent;
#endif /* USE_POLLABLE_EVENT */ #endif /* USE_POLLABLE_EVENT */
PRLock* mThreadLock; PRLock* mThreadLock;
PRBool mThreadRunning; PRBool mThreadRunning;
PRCList mWorkQ; PRIntervalTime mSocketTimeoutInterval;
PRInt32 mSelectFDSetCount; PRCList mWorkQ;
PRPollDesc* mSelectFDSet;
nsSocketTransport** mActiveTransportList; PRInt32 mSelectFDSetCount;
PRPollDesc* mSelectFDSet;
nsSocketTransport** mActiveTransportList;
}; };