Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

267 lines
6.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "net_ws_headers.h"
  7. #include "net_ws_queued_packet_sender.h"
  8. #include "tier1/utlvector.h"
  9. #include "tier1/utlpriorityqueue.h"
  10. #include "tier0/etwprof.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. ConVar net_queued_packet_thread( "net_queued_packet_thread", "1", 0, "Use a high priority thread to send queued packets out instead of sending them each frame." );
  14. ConVar net_queue_trace( "net_queue_trace", "0", 0 );
  15. class CQueuedPacketSender : public CThread, public IQueuedPacketSender
  16. {
  17. public:
  18. CQueuedPacketSender();
  19. ~CQueuedPacketSender();
  20. // IQueuedPacketSender
  21. virtual bool Setup();
  22. virtual void Shutdown();
  23. virtual bool IsRunning() { return CThread::IsAlive(); }
  24. virtual void ClearQueuedPacketsForChannel( INetChannel *pChan );
  25. virtual void QueuePacket( INetChannel *pChan, SOCKET s, const char FAR *buf, int len, const struct sockaddr FAR * to, int tolen, uint32 msecDelay );
  26. virtual bool HasQueuedPackets( const INetChannel *pChan ) const;
  27. private:
  28. // CThread Overrides
  29. virtual bool Start( unsigned int nBytesStack = 0 );
  30. virtual int Run();
  31. private:
  32. class CQueuedPacket
  33. {
  34. public:
  35. uint32 m_unSendTime;
  36. const void *m_pChannel; // We don't actually use the channel
  37. SOCKET m_Socket;
  38. CUtlVector<char> to; // sockaddr
  39. CUtlVector<char> buf;
  40. // We want the list sorted in ascending order, so note that we return > rather than <
  41. static bool LessFunc( CQueuedPacket * const &lhs, CQueuedPacket * const &rhs )
  42. {
  43. return lhs->m_unSendTime > rhs->m_unSendTime;
  44. }
  45. };
  46. CUtlPriorityQueue< CQueuedPacket * > m_QueuedPackets;
  47. CThreadMutex m_QueuedPacketsCS;
  48. CThreadEvent m_hThreadEvent;
  49. volatile bool m_bThreadShouldExit;
  50. };
  51. static CQueuedPacketSender g_QueuedPacketSender;
  52. IQueuedPacketSender *g_pQueuedPackedSender = &g_QueuedPacketSender;
  53. CQueuedPacketSender::CQueuedPacketSender() :
  54. m_QueuedPackets( 0, 0, CQueuedPacket::LessFunc )
  55. {
  56. SetName( "QueuedPacketSender" );
  57. m_bThreadShouldExit = false;
  58. }
  59. CQueuedPacketSender::~CQueuedPacketSender()
  60. {
  61. Shutdown();
  62. }
  63. bool CQueuedPacketSender::Setup()
  64. {
  65. return Start();
  66. }
  67. bool CQueuedPacketSender::Start( unsigned nBytesStack )
  68. {
  69. Shutdown();
  70. if ( CThread::Start( nBytesStack ) )
  71. {
  72. // Ahhh the perfect cross-platformness of the threads library.
  73. #ifdef IS_WINDOWS_PC
  74. SetPriority( THREAD_PRIORITY_HIGHEST );
  75. #elif POSIX
  76. //SetPriority( PRIORITY_MAX );
  77. #endif
  78. m_bThreadShouldExit = false;
  79. return true;
  80. }
  81. else
  82. {
  83. return false;
  84. }
  85. }
  86. void CQueuedPacketSender::Shutdown()
  87. {
  88. if ( !IsAlive() )
  89. return;
  90. m_bThreadShouldExit = true;
  91. m_hThreadEvent.Set();
  92. Join(); // Wait for the thread to exit.
  93. while ( m_QueuedPackets.Count() > 0 )
  94. {
  95. delete m_QueuedPackets.ElementAtHead();
  96. m_QueuedPackets.RemoveAtHead();
  97. }
  98. m_QueuedPackets.Purge();
  99. }
  100. void CQueuedPacketSender::ClearQueuedPacketsForChannel( INetChannel *pChan )
  101. {
  102. AUTO_LOCK( m_QueuedPacketsCS );
  103. for ( int i = m_QueuedPackets.Count()-1; i >= 0; i-- )
  104. {
  105. CQueuedPacket *p = m_QueuedPackets.Element( i );
  106. if ( p->m_pChannel == pChan )
  107. {
  108. m_QueuedPackets.RemoveAt( i );
  109. delete p;
  110. }
  111. }
  112. }
  113. bool CQueuedPacketSender::HasQueuedPackets( const INetChannel *pChan ) const
  114. {
  115. AUTO_LOCK( m_QueuedPacketsCS );
  116. for ( int i = 0; i < m_QueuedPackets.Count(); ++i )
  117. {
  118. const CQueuedPacket *p = m_QueuedPackets.Element( i );
  119. if ( p->m_pChannel == pChan )
  120. {
  121. return true;
  122. }
  123. }
  124. return false;
  125. }
  126. void CQueuedPacketSender::QueuePacket( INetChannel *pChan, SOCKET s, const char FAR *buf, int len, const struct sockaddr FAR * to, int tolen, uint32 msecDelay )
  127. {
  128. AUTO_LOCK( m_QueuedPacketsCS );
  129. // We'll pull all packets we should have sent by now and send them out right away
  130. uint32 msNow = Plat_MSTime();
  131. int nMaxQueuedPackets = 1024;
  132. if ( m_QueuedPackets.Count() < nMaxQueuedPackets )
  133. {
  134. // Add this packet to the queue.
  135. CQueuedPacket *pPacket = new CQueuedPacket;
  136. pPacket->m_unSendTime = msNow + msecDelay;
  137. pPacket->m_Socket = s;
  138. pPacket->m_pChannel = pChan;
  139. pPacket->buf.CopyArray( (char*)buf, len );
  140. pPacket->to.CopyArray( (char*)to, tolen );
  141. m_QueuedPackets.Insert( pPacket );
  142. }
  143. else
  144. {
  145. static int nWarnings = 5;
  146. if ( --nWarnings > 0 )
  147. {
  148. Warning( "CQueuedPacketSender: num queued packets >= nMaxQueuedPackets. Not queueing anymore.\n" );
  149. }
  150. }
  151. // Tell the thread that we have a queued packet.
  152. m_hThreadEvent.Set();
  153. }
  154. extern int NET_SendToImpl( SOCKET s, const char FAR * buf, int len, const struct sockaddr FAR * to, int tolen, int iGameDataLength );
  155. int CQueuedPacketSender::Run()
  156. {
  157. // Normally TT_INFINITE but we wakeup every 50ms just in case.
  158. uint32 waitIntervalNoPackets = 50;
  159. uint32 waitInterval = waitIntervalNoPackets;
  160. while ( 1 )
  161. {
  162. if ( m_hThreadEvent.Wait( waitInterval ) )
  163. {
  164. // Someone signaled the thread. Either we're being told to exit or
  165. // we're being told that a packet was just queued.
  166. if ( m_bThreadShouldExit )
  167. return 0;
  168. }
  169. // Assume nothing to do and that we'll sleep again
  170. waitInterval = waitIntervalNoPackets;
  171. // OK, now send a packet.
  172. {
  173. AUTO_LOCK( m_QueuedPacketsCS );
  174. // We'll pull all packets we should have sent by now and send them out right away
  175. uint32 msNow = Plat_MSTime();
  176. bool bTrace = net_queue_trace.GetInt() == NET_QUEUED_PACKET_THREAD_DEBUG_VALUE;
  177. while ( m_QueuedPackets.Count() > 0 )
  178. {
  179. CQueuedPacket *pPacket = m_QueuedPackets.ElementAtHead();
  180. if ( pPacket->m_unSendTime > msNow )
  181. {
  182. // Sleep until next we need this packet
  183. waitInterval = pPacket->m_unSendTime - msNow;
  184. // Emit ETW events to help with diagnosing network throttling issues as
  185. // these often have a severe effect on load times in Dota.
  186. ETWMark1I( "CQueuedPacketSender::Run sleeping (ms)", waitInterval );
  187. if ( bTrace )
  188. {
  189. Warning( "SQ: sleeping for %u msecs at %f\n", waitInterval, Plat_FloatTime() );
  190. }
  191. break;
  192. }
  193. // If it's a bot, don't do anything. Note: we DO want this code deep here because bots only
  194. // try to send packets when sv_stressbots is set, in which case we want it to act as closely
  195. // as a real player as possible.
  196. sockaddr_in *pInternetAddr = (sockaddr_in*)pPacket->to.Base();
  197. #ifdef _WIN32
  198. if ( pInternetAddr->sin_addr.S_un.S_addr != 0
  199. #else
  200. if ( pInternetAddr->sin_addr.s_addr != 0
  201. #endif
  202. && pInternetAddr->sin_port != 0 )
  203. {
  204. if ( bTrace )
  205. {
  206. Warning( "SQ: sending %d bytes at %f\n", pPacket->buf.Count(), Plat_FloatTime() );
  207. }
  208. NET_SendToImpl
  209. (
  210. pPacket->m_Socket,
  211. pPacket->buf.Base(),
  212. pPacket->buf.Count(),
  213. (sockaddr*)pPacket->to.Base(),
  214. pPacket->to.Count(),
  215. -1
  216. );
  217. }
  218. delete pPacket;
  219. m_QueuedPackets.RemoveAtHead();
  220. }
  221. }
  222. }
  223. }