Counter Strike : Global Offensive Source Code
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.

268 lines
6.5 KiB

  1. //====== Copyright 1996-2005, 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 "tier0/vprof.h"
  9. #include "tier1/utlvector.h"
  10. #include "tier1/utlpriorityqueue.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", FCVAR_ACCESSIBLE_FROM_THREADS );
  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 ns_address &to, 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. ns_address to;
  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. ThreadSetDebugName( GetThreadHandle(), "CQueuedPacketSender" );
  76. #elif POSIX
  77. //SetPriority( PRIORITY_MAX );
  78. #endif
  79. m_bThreadShouldExit = false;
  80. return true;
  81. }
  82. else
  83. {
  84. return false;
  85. }
  86. }
  87. void CQueuedPacketSender::Shutdown()
  88. {
  89. if ( !IsAlive() )
  90. return;
  91. #ifdef _WIN32
  92. if ( !GetThreadHandle() )
  93. {
  94. Msg( "-->Shutdown %p\n", GetThreadHandle() );
  95. }
  96. #endif
  97. m_bThreadShouldExit = true;
  98. m_hThreadEvent.Set();
  99. Join(); // Wait for the thread to exit.
  100. while ( m_QueuedPackets.Count() > 0 )
  101. {
  102. delete m_QueuedPackets.ElementAtHead();
  103. m_QueuedPackets.RemoveAtHead();
  104. }
  105. m_QueuedPackets.Purge();
  106. }
  107. void CQueuedPacketSender::ClearQueuedPacketsForChannel( INetChannel *pChan )
  108. {
  109. AUTO_LOCK( m_QueuedPacketsCS );
  110. for ( int i = m_QueuedPackets.Count()-1; i >= 0; i-- )
  111. {
  112. CQueuedPacket *p = m_QueuedPackets.Element( i );
  113. if ( p->m_pChannel == pChan )
  114. {
  115. m_QueuedPackets.RemoveAt( i );
  116. delete p;
  117. }
  118. }
  119. }
  120. bool CQueuedPacketSender::HasQueuedPackets( const INetChannel *pChan ) const
  121. {
  122. AUTO_LOCK( m_QueuedPacketsCS );
  123. for ( int i = 0; i < m_QueuedPackets.Count(); ++i )
  124. {
  125. const CQueuedPacket *p = m_QueuedPackets.Element( i );
  126. if ( p->m_pChannel == pChan )
  127. {
  128. return true;
  129. }
  130. }
  131. return false;
  132. }
  133. void CQueuedPacketSender::QueuePacket( INetChannel *pChan, SOCKET s, const char FAR *buf, int len, const ns_address &to, uint32 msecDelay )
  134. {
  135. AUTO_LOCK( m_QueuedPacketsCS );
  136. // We'll pull all packets we should have sent by now and send them out right away
  137. uint32 msNow = Plat_MSTime();
  138. int nMaxQueuedPackets = 1024;
  139. if ( m_QueuedPackets.Count() < nMaxQueuedPackets )
  140. {
  141. // Add this packet to the queue.
  142. CQueuedPacket *pPacket = new CQueuedPacket;
  143. pPacket->m_unSendTime = msNow + msecDelay;
  144. pPacket->m_Socket = s;
  145. pPacket->m_pChannel = pChan;
  146. pPacket->buf.CopyArray( (char*)buf, len );
  147. pPacket->to = to;
  148. m_QueuedPackets.Insert( pPacket );
  149. }
  150. else
  151. {
  152. static int nWarnings = 5;
  153. if ( --nWarnings > 0 )
  154. {
  155. Warning( "CQueuedPacketSender: num queued packets >= nMaxQueuedPackets. Not queueing anymore.\n" );
  156. }
  157. }
  158. // Tell the thread that we have a queued packet.
  159. m_hThreadEvent.Set();
  160. }
  161. extern int NET_SendToImpl( SOCKET s, const char FAR * buf, int len, const ns_address &to, int iGameDataLength );
  162. int CQueuedPacketSender::Run()
  163. {
  164. // Normally TT_INFINITE but we wakeup every 500ms just in case.
  165. uint32 waitIntervalNoPackets = 500;
  166. uint32 waitInterval = waitIntervalNoPackets;
  167. while ( 1 )
  168. {
  169. m_hThreadEvent.Wait( waitInterval );
  170. {
  171. // Someone signaled the thread. Either we're being told to exit or
  172. // we're being told that a packet was just queued.
  173. if ( m_bThreadShouldExit )
  174. return 0;
  175. }
  176. // Assume nothing to do and that we'll sleep again
  177. waitInterval = waitIntervalNoPackets;
  178. // OK, now send a packet.
  179. {
  180. SNPROF("NET_SendToImpl");
  181. AUTO_LOCK( m_QueuedPacketsCS );
  182. // We'll pull all packets we should have sent by now and send them out right away
  183. uint32 msNow = Plat_MSTime();
  184. bool bTrace = net_queue_trace.GetInt() == NET_QUEUED_PACKET_THREAD_DEBUG_VALUE;
  185. while ( m_QueuedPackets.Count() > 0 )
  186. {
  187. CQueuedPacket *pPacket = m_QueuedPackets.ElementAtHead();
  188. if ( pPacket->m_unSendTime > msNow )
  189. {
  190. // Sleep until next we need this packet
  191. waitInterval = pPacket->m_unSendTime - msNow;
  192. if ( bTrace )
  193. {
  194. Warning( "SQ: sleeping for %u msecs at %f\n", waitInterval, Plat_FloatTime() );
  195. }
  196. break;
  197. }
  198. // If it's a bot, don't do anything. Note: we DO want this code deep here because bots only
  199. // try to send packets when sv_stressbots is set, in which case we want it to act as closely
  200. // as a real player as possible.
  201. if ( !pPacket->to.IsNull() )
  202. {
  203. if ( bTrace )
  204. {
  205. Warning( "SQ: sending %d bytes at %f\n", pPacket->buf.Count(), Plat_FloatTime() );
  206. }
  207. NET_SendToImpl
  208. (
  209. pPacket->m_Socket,
  210. pPacket->buf.Base(),
  211. pPacket->buf.Count(),
  212. pPacket->to,
  213. -1
  214. );
  215. }
  216. delete pPacket;
  217. m_QueuedPackets.RemoveAtHead();
  218. }
  219. }
  220. }
  221. }