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.

421 lines
9.3 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "sentence.h"
  9. #include "wavefile.h"
  10. #include "tier2/riff.h"
  11. #include "filesystem.h"
  12. #include <io.h>
  13. #include <fcntl.h>
  14. #include <sys/types.h>
  15. #include "IFileLoader.h"
  16. bool SceneManager_LoadSentenceFromWavFileUsingIO( char const *wavfile, CSentence& sentence, IFileReadBinary& io );
  17. //-----------------------------------------------------------------------------
  18. // Purpose: Implements the RIFF i/o interface on stdio
  19. //-----------------------------------------------------------------------------
  20. class ThreadIOReadBinary : public IFileReadBinary
  21. {
  22. public:
  23. int open( const char *pFileName )
  24. {
  25. char filename[ 512 ];
  26. // POSSIBLE BUG: THIS MIGHT NOT BE THREAD SAFE!!!
  27. filesystem->RelativePathToFullPath( pFileName, "GAME", filename, sizeof( filename ) );
  28. return (int)_open( filename, _O_BINARY | _O_RDONLY );
  29. }
  30. int read( void *pOutput, int size, int file )
  31. {
  32. if ( !file )
  33. return 0;
  34. return _read( file, pOutput, size );
  35. }
  36. void seek( int file, int pos )
  37. {
  38. if ( !file )
  39. return;
  40. _lseek( file, pos, SEEK_SET );
  41. }
  42. unsigned int tell( int file )
  43. {
  44. if ( !file )
  45. return 0;
  46. return _tell( file );
  47. }
  48. unsigned int size( int file )
  49. {
  50. if ( !file )
  51. return 0;
  52. long curpos = tell( file );
  53. _lseek( file, 0, SEEK_END );
  54. int s = tell( file );
  55. _lseek( file, curpos, SEEK_SET );
  56. return s;
  57. }
  58. void close( int file )
  59. {
  60. if ( !file )
  61. return;
  62. _close( file );
  63. }
  64. };
  65. //-----------------------------------------------------------------------------
  66. // Purpose: All wavefile I/O occurs on a thread
  67. //-----------------------------------------------------------------------------
  68. class CFileLoaderThread : public IFileLoader
  69. {
  70. public:
  71. struct SentenceRequest
  72. {
  73. SentenceRequest()
  74. {
  75. filename[ 0 ] = 0;
  76. sentence.Reset();
  77. wavefile = NULL;
  78. valid = false;
  79. }
  80. bool valid;
  81. char filename[ 256 ];
  82. CSentence sentence;
  83. CWaveFile *wavefile;
  84. };
  85. // Construction
  86. CFileLoaderThread( void );
  87. virtual ~CFileLoaderThread( void );
  88. // Sockets add/remove themselves via their constructor
  89. virtual void AddWaveFilesToThread( CUtlVector< CWaveFile * >& wavefiles );
  90. // Lock changes to wavefile list, etc.
  91. virtual void Lock( void );
  92. // Unlock wavefile list, etc.
  93. virtual void Unlock( void );
  94. // Retrieve handle to shutdown event
  95. virtual HANDLE GetShutdownHandle( void );
  96. // Caller should call lock before accessing any of these methods and unlock afterwards!!!
  97. virtual int ProcessCompleted();
  98. int DoThreadWork();
  99. virtual void Start();
  100. virtual int GetPendingLoadCount();
  101. private:
  102. // Critical section used for synchronizing access to wavefile list
  103. CRITICAL_SECTION cs;
  104. CRITICAL_SECTION m_CountCS;
  105. // List of wavefiles we are listening on
  106. CUtlVector< SentenceRequest * > m_FileList;
  107. CUtlVector< SentenceRequest * > m_Pending;
  108. CUtlVector< SentenceRequest * > m_Completed;
  109. // Thread handle
  110. HANDLE m_hThread;
  111. // Thread id
  112. DWORD m_nThreadId;
  113. // Event to set when we want to tell the thread to shut itself down
  114. HANDLE m_hShutdown;
  115. ThreadIOReadBinary m_ThreadIO;
  116. bool m_bLocked;
  117. int m_nTotalAdds;
  118. int m_nTotalPending;
  119. int m_nTotalProcessed;
  120. int m_nTotalCompleted;
  121. HANDLE m_hNewItems;
  122. };
  123. // Singleton handler
  124. static CFileLoaderThread g_WaveLoader;
  125. extern IFileLoader *fileloader = &g_WaveLoader;
  126. int CFileLoaderThread::DoThreadWork()
  127. {
  128. int i;
  129. // Check for shutdown event
  130. if ( WAIT_OBJECT_0 == WaitForSingleObject( GetShutdownHandle(), 0 ) )
  131. {
  132. return 0;
  133. }
  134. // No changes to list right now
  135. Lock();
  136. // Move new items to work list
  137. int newItems = m_FileList.Count();
  138. for ( i = 0; i < newItems; i++ )
  139. {
  140. // Move to pending and issue async i/o calls
  141. m_Pending.AddToHead( m_FileList[ i ] );
  142. EnterCriticalSection( &m_CountCS );
  143. m_nTotalPending++;
  144. LeaveCriticalSection( &m_CountCS );
  145. }
  146. m_FileList.RemoveAll();
  147. // Done adding new work items
  148. Unlock();
  149. int remaining = m_Pending.Count();
  150. if ( !remaining )
  151. return 1;
  152. int workitems = remaining; // min( remaining, 1000 );
  153. CUtlVector< SentenceRequest * > transfer;
  154. for ( i = 0; i < workitems; i++ )
  155. {
  156. SentenceRequest *r = m_Pending[ 0 ];
  157. m_Pending.Remove( 0 );
  158. transfer.AddToTail( r );
  159. // Do the work
  160. EnterCriticalSection( &m_CountCS );
  161. m_nTotalProcessed++;
  162. LeaveCriticalSection( &m_CountCS );
  163. Lock();
  164. bool load = !r->wavefile->HasLoadedSentenceInfo();
  165. Unlock();
  166. if ( load )
  167. {
  168. r->valid = SceneManager_LoadSentenceFromWavFileUsingIO( r->filename, r->sentence, m_ThreadIO );
  169. }
  170. else
  171. {
  172. r->valid = true;
  173. }
  174. if ( WaitForSingleObject( m_hNewItems, 0 ) == WAIT_OBJECT_0 )
  175. {
  176. ResetEvent( m_hNewItems );
  177. break;
  178. }
  179. }
  180. // Now move to completed list
  181. Lock();
  182. int c = transfer.Count();
  183. for ( i = 0; i < c; ++i )
  184. {
  185. SentenceRequest *r = transfer[ i ];
  186. if ( r->valid )
  187. {
  188. m_nTotalCompleted++;
  189. m_Completed.AddToTail( r );
  190. }
  191. else
  192. {
  193. delete r;
  194. }
  195. }
  196. Unlock();
  197. return 1;
  198. }
  199. int CFileLoaderThread::ProcessCompleted()
  200. {
  201. Lock();
  202. int c = m_Completed.Count();
  203. for ( int i = c - 1; i >= 0 ; i-- )
  204. {
  205. SentenceRequest *r = m_Completed[ i ];
  206. if ( !r->wavefile->HasLoadedSentenceInfo() )
  207. {
  208. r->wavefile->SetThreadLoadedSentence( r->sentence );
  209. }
  210. delete r;
  211. }
  212. m_Completed.RemoveAll();
  213. Unlock();
  214. return c;
  215. }
  216. //-----------------------------------------------------------------------------
  217. // Purpose: Main winsock processing thread
  218. // Input : threadobject -
  219. // Output : static DWORD WINAPI
  220. //-----------------------------------------------------------------------------
  221. static DWORD WINAPI FileLoaderThreadFunc( LPVOID threadobject )
  222. {
  223. // Get pointer to CFileLoaderThread object
  224. CFileLoaderThread *wavefilethread = ( CFileLoaderThread * )threadobject;
  225. Assert( wavefilethread );
  226. if ( !wavefilethread )
  227. {
  228. return 0;
  229. }
  230. // Keep looking for data until shutdown event is triggered
  231. while ( 1 )
  232. {
  233. if( !wavefilethread->DoThreadWork() )
  234. break;
  235. // Yield a small bit of time to main app
  236. Sleep( 10 );
  237. }
  238. ExitThread( 0 );
  239. return 0;
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose: Construction
  243. //-----------------------------------------------------------------------------
  244. CFileLoaderThread::CFileLoaderThread( void )
  245. {
  246. m_nTotalAdds = 0;
  247. m_nTotalProcessed = 0;
  248. m_nTotalCompleted = 0;
  249. m_nTotalPending = 0;
  250. m_bLocked = false;
  251. InitializeCriticalSection( &cs );
  252. InitializeCriticalSection( &m_CountCS );
  253. m_hShutdown = CreateEvent( NULL, TRUE, FALSE, NULL );
  254. Assert( m_hShutdown );
  255. m_hThread = NULL;
  256. m_hNewItems = CreateEvent( NULL, TRUE, FALSE, NULL );
  257. Start();
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose:
  261. //-----------------------------------------------------------------------------
  262. void CFileLoaderThread::Start()
  263. {
  264. m_hThread = CreateThread( NULL, 0, FileLoaderThreadFunc, (void *)this, 0, &m_nThreadId );
  265. Assert( m_hThread );
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose:
  269. //-----------------------------------------------------------------------------
  270. CFileLoaderThread::~CFileLoaderThread( void )
  271. {
  272. Lock();
  273. {
  274. SetEvent( m_hShutdown );
  275. Sleep( 2 );
  276. TerminateThread( m_hThread, 0 );
  277. }
  278. Unlock();
  279. // Kill the wavefile
  280. //!! need to validate this line
  281. // Assert( !m_FileList );
  282. CloseHandle( m_hThread );
  283. CloseHandle( m_hShutdown );
  284. DeleteCriticalSection( &cs );
  285. DeleteCriticalSection( &m_CountCS );
  286. CloseHandle( m_hNewItems );
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Purpose: Returns handle of shutdown event
  290. // Output : HANDLE
  291. //-----------------------------------------------------------------------------
  292. HANDLE CFileLoaderThread::GetShutdownHandle( void )
  293. {
  294. return m_hShutdown;
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose: Locks object and adds wavefile to thread
  298. // Input : *wavefile -
  299. //-----------------------------------------------------------------------------
  300. void CFileLoaderThread::AddWaveFilesToThread( CUtlVector< CWaveFile * >& wavefiles )
  301. {
  302. Lock();
  303. int c = wavefiles.Count();
  304. for ( int i = 0; i < c; i++ )
  305. {
  306. SentenceRequest *request = new SentenceRequest;
  307. request->wavefile = wavefiles[ i ];
  308. Q_strncpy( request->filename, request->wavefile->GetFileName(), sizeof( request->filename ) );
  309. m_FileList.AddToTail( request );
  310. m_nTotalAdds++;
  311. }
  312. SetEvent( m_hNewItems );
  313. Unlock();
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose:
  317. //-----------------------------------------------------------------------------
  318. void CFileLoaderThread::Lock( void )
  319. {
  320. EnterCriticalSection( &cs );
  321. Assert( !m_bLocked );
  322. m_bLocked = true;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose:
  326. //-----------------------------------------------------------------------------
  327. void CFileLoaderThread::Unlock( void )
  328. {
  329. Assert( m_bLocked );
  330. m_bLocked = false;
  331. LeaveCriticalSection( &cs );
  332. }
  333. int CFileLoaderThread::GetPendingLoadCount()
  334. {
  335. int iret = 0;
  336. EnterCriticalSection( &m_CountCS );
  337. iret = m_nTotalPending - m_nTotalProcessed;
  338. LeaveCriticalSection( &m_CountCS );
  339. return iret;
  340. }