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.

275 lines
5.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include <windows.h>
  7. #include "imysqlwrapper.h"
  8. #include "mysql_async.h"
  9. #include "utllinkedlist.h"
  10. static char* CopyString( const char *pStr )
  11. {
  12. char *pRet = new char[ strlen( pStr ) + 1 ];
  13. strcpy( pRet, pStr );
  14. return pRet;
  15. }
  16. class CMySQLAsync : public IMySQLAsync
  17. {
  18. public:
  19. CMySQLAsync()
  20. {
  21. m_hThread = NULL;
  22. m_pSQL = NULL;
  23. m_hExitEvent = CreateEvent( NULL, true, false, NULL ); // Use manual reset because we want it to cascade out without
  24. // resetting the event if it gets set.
  25. m_hPendingQueryEvent = CreateEvent( NULL, false, false, NULL );
  26. m_hQueryResultsEvent = CreateEvent( NULL, false, false, NULL );
  27. InitializeCriticalSection( &m_ExecuteQueryCS );
  28. InitializeCriticalSection( &m_PendingQueryCS );
  29. }
  30. ~CMySQLAsync()
  31. {
  32. Term();
  33. CloseHandle( m_hExitEvent );
  34. CloseHandle( m_hPendingQueryEvent );
  35. CloseHandle( m_hQueryResultsEvent );
  36. DeleteCriticalSection( &m_ExecuteQueryCS );
  37. DeleteCriticalSection( &m_PendingQueryCS );
  38. }
  39. virtual void Release()
  40. {
  41. delete this;
  42. }
  43. virtual IMySQLRowSet* ExecuteBlocking( const char *pStr )
  44. {
  45. IMySQLRowSet *pRet;
  46. EnterCriticalSection( &m_ExecuteQueryCS );
  47. m_pSQL->Execute( pStr );
  48. pRet = m_pSQL->DuplicateRowSet();
  49. LeaveCriticalSection( &m_ExecuteQueryCS );
  50. return pRet;
  51. }
  52. virtual void Execute( const char *pStr, void *pUserData )
  53. {
  54. EnterCriticalSection( &m_PendingQueryCS );
  55. CPendingQuery query;
  56. query.m_pStr = CopyString( pStr );
  57. query.m_pUserData = pUserData;
  58. query.m_Timer.Start();
  59. m_PendingQueries.AddToTail( query );
  60. SetEvent( m_hPendingQueryEvent );
  61. LeaveCriticalSection( &m_PendingQueryCS );
  62. }
  63. virtual bool GetNextResults( CQueryResults &results )
  64. {
  65. results.m_pResults = NULL;
  66. if ( WaitForSingleObject( m_hQueryResultsEvent, 0 ) == WAIT_OBJECT_0 )
  67. {
  68. EnterCriticalSection( &m_PendingQueryCS );
  69. Assert( m_QueryResults.Count() > 0 );
  70. int iHead = m_QueryResults.Head();
  71. results = m_QueryResults[iHead];
  72. m_QueryResults.Remove( iHead );
  73. if ( m_QueryResults.Count() > 0 )
  74. SetEvent( m_hQueryResultsEvent );
  75. LeaveCriticalSection( &m_PendingQueryCS );
  76. return true;
  77. }
  78. else
  79. {
  80. return false;
  81. }
  82. }
  83. bool Init( IMySQL *pSQL )
  84. {
  85. Term();
  86. DWORD dwThreadID;
  87. m_hThread = CreateThread( NULL, 0, &CMySQLAsync::StaticThreadFn, this, 0, &dwThreadID );
  88. if ( m_hThread )
  89. {
  90. m_pSQL = pSQL;
  91. return true;
  92. }
  93. else
  94. {
  95. return false;
  96. }
  97. }
  98. void Term()
  99. {
  100. // Stop the thread.
  101. if ( m_hThread )
  102. {
  103. // Delete all our queries.
  104. SetEvent( m_hExitEvent );
  105. WaitForSingleObject( m_hThread, INFINITE );
  106. CloseHandle( m_hThread );
  107. m_hThread = NULL;
  108. }
  109. // Delete leftover queries.
  110. FOR_EACH_LL( m_PendingQueries, iPendingQuery )
  111. {
  112. delete [] m_PendingQueries[iPendingQuery].m_pStr;
  113. }
  114. m_PendingQueries.Purge();
  115. FOR_EACH_LL( m_QueryResults, i )
  116. {
  117. m_QueryResults[i].m_pResults->Release();
  118. }
  119. m_QueryResults.Purge();
  120. if ( m_pSQL )
  121. {
  122. m_pSQL->Release();
  123. m_pSQL = NULL;
  124. }
  125. }
  126. private:
  127. DWORD ThreadFn()
  128. {
  129. HANDLE hEvents[2] = { m_hExitEvent, m_hPendingQueryEvent };
  130. //
  131. while ( 1 )
  132. {
  133. int ret = WaitForMultipleObjects( ARRAYSIZE( hEvents ), hEvents, false, INFINITE );
  134. if ( ret == WAIT_OBJECT_0 )
  135. break;
  136. if ( ret == WAIT_OBJECT_0+1 )
  137. {
  138. // A new string has been queued up for us to execute.
  139. EnterCriticalSection( &m_PendingQueryCS );
  140. Assert( m_PendingQueries.Count() > 0 );
  141. int iHead = m_PendingQueries.Head();
  142. CPendingQuery pending = m_PendingQueries[iHead];
  143. m_PendingQueries.Remove( iHead );
  144. // Set the pending query event if there are more queries waiting to run.
  145. if ( m_PendingQueries.Count() > 0 )
  146. SetEvent( m_hPendingQueryEvent );
  147. LeaveCriticalSection( &m_PendingQueryCS );
  148. // Run the query.
  149. EnterCriticalSection( &m_ExecuteQueryCS );
  150. CQueryResults results;
  151. results.m_pResults = NULL;
  152. results.m_pUserData = pending.m_pUserData;
  153. results.m_ExecuteTime.Init();
  154. pending.m_Timer.End();
  155. results.m_QueueTime = pending.m_Timer.GetDuration();
  156. CFastTimer executeTimer;
  157. executeTimer.Start();
  158. if ( m_pSQL->Execute( pending.m_pStr ) == 0 )
  159. {
  160. executeTimer.End();
  161. results.m_ExecuteTime = executeTimer.GetDuration();
  162. results.m_pResults = m_pSQL->DuplicateRowSet();
  163. }
  164. delete pending.m_pStr;
  165. LeaveCriticalSection( &m_ExecuteQueryCS );
  166. // Store the results.
  167. EnterCriticalSection( &m_PendingQueryCS );
  168. m_QueryResults.AddToTail( results );
  169. SetEvent( m_hQueryResultsEvent );
  170. LeaveCriticalSection( &m_PendingQueryCS );
  171. }
  172. }
  173. return 0;
  174. }
  175. static DWORD WINAPI StaticThreadFn( LPVOID lpParameter )
  176. {
  177. return ((CMySQLAsync*)lpParameter)->ThreadFn();
  178. }
  179. private:
  180. HANDLE m_hThread;
  181. HANDLE m_hExitEvent;
  182. HANDLE m_hPendingQueryEvent; // Signaled when a new query is added.
  183. HANDLE m_hQueryResultsEvent;
  184. IMySQL *m_pSQL;
  185. CRITICAL_SECTION m_PendingQueryCS;
  186. CRITICAL_SECTION m_ExecuteQueryCS;
  187. // Outgoing query results. New ones are added to the tail.
  188. CUtlLinkedList<CQueryResults, int> m_QueryResults;
  189. // New ones added to the tail.
  190. class CPendingQuery
  191. {
  192. public:
  193. char *m_pStr;
  194. void *m_pUserData;
  195. CFastTimer m_Timer; // Times how long this query is in the queue.
  196. };
  197. CUtlLinkedList<CPendingQuery,int> m_PendingQueries;
  198. };
  199. IMySQLAsync* CreateMySQLAsync( IMySQL *pSQL )
  200. {
  201. CMySQLAsync *pRet = new CMySQLAsync;
  202. if ( pRet->Init( pSQL ) )
  203. {
  204. return pRet;
  205. }
  206. else
  207. {
  208. delete pRet;
  209. return NULL;
  210. }
  211. }