From 3d8af6460e6170bb2bc688f848fb342eed454637 Mon Sep 17 00:00:00 2001 From: Fletcher Dunn Date: Wed, 5 Jan 2022 11:01:25 -0800 Subject: [PATCH] Don't "linger" if it isn't necessary. When the "linger" mode is requested when closing a connection, check if we actually need to do so. If not, then immediately enter the FinWait state. Otherwise, schedule an immediate wakeup. The context here is that there's kind of sharp edge with closing connections and then immediately terminating the app. This is not as TCP connection, so we cannot hand the connection over to the OS to clean it up. The next change will expand on this. P4:6986508 --- .../steamnetworkingsockets_connections.cpp | 40 ++++++++++++++----- .../steamnetworkingsockets_connections.h | 3 ++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp index 63e0288..3ccea87 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.cpp @@ -2323,7 +2323,8 @@ void CSteamNetworkConnectionBase::APICloseConnection( int nReason, const char *p } } - // Check our state + // Check our state to see how to spew, and handle a few exceptional cases + // where we don't transition to FinWait switch ( GetState() ) { case k_ESteamNetworkingConnectionState_Dead: @@ -2337,29 +2338,41 @@ void CSteamNetworkConnectionBase::APICloseConnection( int nReason, const char *p case k_ESteamNetworkingConnectionState_ClosedByPeer: case k_ESteamNetworkingConnectionState_ProblemDetectedLocally: SpewVerbose( "[%s] cleaned up\n", GetDescription() ); - ConnectionState_FinWait(); break; case k_ESteamNetworkingConnectionState_Connecting: case k_ESteamNetworkingConnectionState_FindingRoute: SpewMsg( "[%s] closed by app before we got connected (%d) %s\n", GetDescription(), (int)m_eEndReason, m_szEndDebug ); - ConnectionState_FinWait(); break; case k_ESteamNetworkingConnectionState_Connected: if ( bEnableLinger ) { - SpewMsg( "[%s] closed by app, entering linger state (%d) %s\n", GetDescription(), (int)m_eEndReason, m_szEndDebug ); - SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp(); - SetState( k_ESteamNetworkingConnectionState_Linger, usecNow ); + if ( BReadyToExitLingerState() ) + { + SpewMsg( "[%s] closed by app, linger requested but not needed (%d) %s\n", GetDescription(), (int)m_eEndReason, m_szEndDebug ); + } + else + { + SpewMsg( "[%s] closed by app, entering linger state (%d) %s\n", GetDescription(), (int)m_eEndReason, m_szEndDebug ); + SteamNetworkingMicroseconds usecNow = SteamNetworkingSockets_GetLocalTimestamp(); + SetState( k_ESteamNetworkingConnectionState_Linger, usecNow ); + SetNextThinkTimeASAP(); + return; + } } else { SpewMsg( "[%s] closed by app (%d) %s\n", GetDescription(), (int)m_eEndReason, m_szEndDebug ); - ConnectionState_FinWait(); } break; } + + // Enter the FinWait state. Connection-specific and transport code should + // watch for this transition and will send any cleanup packets, and enter + // a state where we wait for the peer to acknowledge and/or retry the + // close message. + ConnectionState_FinWait(); } void CSteamNetworkConnectionBase::SetState( ESteamNetworkingConnectionState eNewState, SteamNetworkingMicroseconds usecNow ) @@ -3327,9 +3340,8 @@ void CSteamNetworkConnectionBase::Think( SteamNetworkingMicroseconds usecNow ) case k_ESteamNetworkingConnectionState_Linger: // Have we sent everything we wanted to? - if ( m_senderState.m_cbPendingReliable == 0 && m_senderState.m_cbSentUnackedReliable == 0 ) + if ( BReadyToExitLingerState() ) { - Assert( m_senderState.m_listReadyRetryReliableRange.IsEmpty() ); // Close the connection ASAP ConnectionState_FinWait(); @@ -3468,6 +3480,16 @@ void CSteamNetworkConnectionBase::Think( SteamNetworkingMicroseconds usecNow ) #undef UpdateMinThinkTime } +bool CSteamNetworkConnectionBase::BReadyToExitLingerState() const +{ + if ( m_senderState.m_cbPendingReliable == 0 && m_senderState.m_cbSentUnackedReliable == 0 ) + { + Assert( m_senderState.m_listReadyRetryReliableRange.IsEmpty() ); + return true; + } + return false; +} + void CSteamNetworkConnectionBase::ThinkConnection( SteamNetworkingMicroseconds usecNow ) { } diff --git a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h index 4881cda..7121e1d 100644 --- a/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h +++ b/src/steamnetworkingsockets/clientlib/steamnetworkingsockets_connections.h @@ -884,6 +884,9 @@ private: /// timeouts. SteamNetworkingMicroseconds m_usecWhenEnteredConnectionState; + /// Return true if we're ready to exit the linger state and go to FinWait + bool BReadyToExitLingerState() const; + // !DEBUG! Log of packets we sent. #ifdef SNP_ENABLE_PACKETSENDLOG struct PacketSendLog