From aaff8f1fe9c9eab51d0107c65ecaceb0c4935756 Mon Sep 17 00:00:00 2001 From: "rpotts%netscape.com" Date: Sat, 6 Nov 1999 01:32:34 +0000 Subject: [PATCH] bug #7428. Added support for connection timeout. Now if a connection cannot be made within 35 seconds, the transport times out. --- netwerk/base/public/netCore.h | 2 +- netwerk/base/src/nsSocketTransport.cpp | 66 +++++++- netwerk/base/src/nsSocketTransport.h | 7 + netwerk/base/src/nsSocketTransportService.cpp | 151 +++++++++++------- netwerk/base/src/nsSocketTransportService.h | 35 +++- 5 files changed, 189 insertions(+), 72 deletions(-) diff --git a/netwerk/base/public/netCore.h b/netwerk/base/public/netCore.h index 881500225e0..147fef2bb50 100644 --- a/netwerk/base/public/netCore.h +++ b/netwerk/base/public/netCore.h @@ -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 \ diff --git a/netwerk/base/src/nsSocketTransport.cpp b/netwerk/base/src/nsSocketTransport.cpp index 97992002e97..788c870e298 100644 --- a/netwerk/base/src/nsSocketTransport.cpp +++ b/netwerk/base/src/nsSocketTransport.cpp @@ -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... diff --git a/netwerk/base/src/nsSocketTransport.h b/netwerk/base/src/nsSocketTransport.h index 0279ad81289..647fc692cd1 100644 --- a/netwerk/base/src/nsSocketTransport.h +++ b/netwerk/base/src/nsSocketTransport.h @@ -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 mDNSRequest; nsCOMPtr mEventSink; char* mHostName; + PRIntervalTime mLastActiveTime; PRCList mListLink; PRUint32 mLoadAttributes; PRLock* mLock; diff --git a/netwerk/base/src/nsSocketTransportService.cpp b/netwerk/base/src/nsSocketTransportService.cpp index c2a718e35f2..903661a2ac8 100644 --- a/netwerk/base/src/nsSocketTransportService.cpp +++ b/netwerk/base/src/nsSocketTransportService.cpp @@ -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... */ diff --git a/netwerk/base/src/nsSocketTransportService.h b/netwerk/base/src/nsSocketTransportService.h index 11ef48c7166..2151aef4d3d 100644 --- a/netwerk/base/src/nsSocketTransportService.h +++ b/netwerk/base/src/nsSocketTransportService.h @@ -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; };