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.

1348 lines
33 KiB

  1. //========== Copyright � 2005, Valve Corporation, All rights reserved. ========
  2. //
  3. // Purpose: CBaseFileSystem Async Operation
  4. //
  5. // The CBaseFileSystem methods implement the IFileSystem
  6. // asynchronous entry points. The model for reads currently is a
  7. // callback model where the callback can take place either in the
  8. // context of the main thread or the worker thread. It would be
  9. // easy to do a polled model later. Async operations return a
  10. // handle which is used to refer to the operation later. The
  11. // handle is actually a pointer to a reference counted "job"
  12. // object that holds all the context, status, and results of an
  13. // operation.
  14. //
  15. //=============================================================================
  16. #include <limits.h>
  17. #if defined( _WIN32 ) && !defined( _X360 )
  18. #define WIN32_LEAN_AND_MEAN
  19. #include <windows.h>
  20. #endif
  21. #include "tier1/convar.h"
  22. #include "vstdlib/jobthread.h"
  23. #include "tier1/utlmap.h"
  24. #include "tier1/utlbuffer.h"
  25. #include "tier0/icommandline.h"
  26. #include "vstdlib/random.h"
  27. #include "basefilesystem.h"
  28. // VCR mode for now is handled by not running async. This is primarily for
  29. // performance reasons. VCR mode would preclude the use of a lock-free job
  30. // retrieval. Can change if need in future, but it's best to do so if needed,
  31. // and to make it a deliberate compile time choice to keep the fast path.
  32. #undef WaitForSingleObject
  33. // memdbgon must be the last include file in a .cpp file!!!
  34. #include "tier0/memdbgon.h"
  35. //-----------------------------------------------------------------------------
  36. //
  37. //-----------------------------------------------------------------------------
  38. ConVar async_mode( "async_mode", "0", 0, "Set the async filesystem mode (0 = async, 1 = synchronous)" );
  39. //-----------------------------------------------------------------------------
  40. // Async Modes
  41. //-----------------------------------------------------------------------------
  42. enum FSAsyncMode_t
  43. {
  44. FSAM_ASYNC,
  45. FSAM_SYNC,
  46. };
  47. #ifndef DISABLE_ASYNC
  48. ConVar async_simulate_delay( "async_simulate_delay", "0", 0, "Simulate a delay of up to a set msec per file operation" );
  49. ConVar async_allow_held_files( "async_allow_held_files", "1", 0, "Allow AsyncBegin/EndRead()" );
  50. static FSAsyncMode_t GetAsyncMode( void )
  51. {
  52. return (FSAsyncMode_t)( async_mode.GetInt() );
  53. }
  54. static bool AsyncAllowHeldFiles( void )
  55. {
  56. return async_allow_held_files.GetBool();
  57. }
  58. CON_COMMAND( async_suspend, "" )
  59. {
  60. BaseFileSystem()->AsyncSuspend();
  61. }
  62. CON_COMMAND( async_resume, "" )
  63. {
  64. BaseFileSystem()->AsyncResume();
  65. }
  66. #else
  67. FORCEINLINE static FSAsyncMode_t GetAsyncMode( void )
  68. {
  69. return FSAM_SYNC;
  70. }
  71. static bool AsyncAllowHeldFiles( void )
  72. {
  73. return false;
  74. }
  75. #endif
  76. FORCEINLINE static void SimulateDelay( void )
  77. {
  78. #ifndef DISABLE_ASYNC
  79. if ( async_simulate_delay.GetInt() == 0 || ThreadInMainThread() )
  80. {
  81. }
  82. else
  83. {
  84. ThreadSleep( RandomInt( 1, async_simulate_delay.GetInt() ) );
  85. }
  86. #endif
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Need to support old external. New implementation has less granular priority for efficiency
  90. //-----------------------------------------------------------------------------
  91. inline JobPriority_t ConvertPriority( int iFilesystemPriority )
  92. {
  93. if ( iFilesystemPriority == 0 )
  94. {
  95. return JP_NORMAL;
  96. }
  97. else if ( iFilesystemPriority > 0 )
  98. {
  99. return JP_HIGH;
  100. }
  101. return JP_LOW;
  102. }
  103. //-----------------------------------------------------------------------------
  104. //
  105. // Support for holding files open
  106. //
  107. //-----------------------------------------------------------------------------
  108. struct AsyncOpenedFile_t : CRefCounted<CRefCountServiceNoDelete> // no mutex needed, under control of CAsyncOpenedFiles
  109. {
  110. AsyncOpenedFile_t() : hFile(FILESYSTEM_INVALID_HANDLE) {}
  111. FileHandle_t hFile;
  112. };
  113. class CAsyncOpenedFiles
  114. {
  115. public:
  116. CAsyncOpenedFiles()
  117. {
  118. m_map.SetLessFunc( CaselessStringLessThan );
  119. }
  120. FSAsyncFile_t FindOrAdd( const char *pszFilename )
  121. {
  122. char szFixedName[MAX_FILEPATH];
  123. Q_strncpy( szFixedName, pszFilename, sizeof( szFixedName ) );
  124. Q_FixSlashes( szFixedName );
  125. Assert( (int)FS_INVALID_ASYNC_FILE == m_map.InvalidIndex() );
  126. AUTO_LOCK_FM( m_mutex );
  127. int iEntry = m_map.Find( szFixedName );
  128. if ( iEntry == m_map.InvalidIndex() )
  129. {
  130. iEntry = m_map.Insert( strdup( szFixedName ), new AsyncOpenedFile_t );
  131. }
  132. else
  133. {
  134. m_map[iEntry]->AddRef();
  135. }
  136. return (FSAsyncFile_t)iEntry;
  137. }
  138. FSAsyncFile_t Find( const char *pszFilename )
  139. {
  140. char szFixedName[MAX_FILEPATH];
  141. Q_strncpy( szFixedName, pszFilename, sizeof( szFixedName ) );
  142. Q_FixSlashes( szFixedName );
  143. AUTO_LOCK_FM( m_mutex );
  144. int iEntry = m_map.Find( szFixedName );
  145. if ( iEntry != m_map.InvalidIndex() )
  146. {
  147. m_map[iEntry]->AddRef();
  148. }
  149. return (FSAsyncFile_t)iEntry;
  150. }
  151. AsyncOpenedFile_t *Get( FSAsyncFile_t item )
  152. {
  153. if ( item == FS_INVALID_ASYNC_FILE)
  154. {
  155. return NULL;
  156. }
  157. AUTO_LOCK_FM( m_mutex );
  158. int iEntry = (CUtlMap<CUtlString, AsyncOpenedFile_t>::IndexType_t)(int)item;
  159. Assert( m_map.IsValidIndex( iEntry ) );
  160. m_map[iEntry]->AddRef();
  161. return m_map[iEntry];
  162. }
  163. void AddRef( FSAsyncFile_t item )
  164. {
  165. if ( item == FS_INVALID_ASYNC_FILE)
  166. {
  167. return;
  168. }
  169. AUTO_LOCK_FM( m_mutex );
  170. int iEntry = (CUtlMap<CUtlString, AsyncOpenedFile_t>::IndexType_t)(int)item;
  171. Assert( m_map.IsValidIndex( iEntry ) );
  172. m_map[iEntry]->AddRef();
  173. }
  174. void Release( FSAsyncFile_t item )
  175. {
  176. if ( item == FS_INVALID_ASYNC_FILE)
  177. {
  178. return;
  179. }
  180. AUTO_LOCK_FM( m_mutex );
  181. int iEntry = (CUtlMap<CUtlString, AsyncOpenedFile_t>::IndexType_t)(int)item;
  182. Assert( m_map.IsValidIndex( iEntry ) );
  183. if ( m_map[iEntry]->Release() == 0 )
  184. {
  185. if ( m_map[iEntry]->hFile != FILESYSTEM_INVALID_HANDLE )
  186. {
  187. BaseFileSystem()->Close( m_map[iEntry]->hFile );
  188. }
  189. delete m_map[iEntry];
  190. delete m_map.Key( iEntry );
  191. m_map.RemoveAt( iEntry );
  192. }
  193. }
  194. private:
  195. CThreadFastMutex m_mutex;
  196. CUtlMap<const char *, AsyncOpenedFile_t *> m_map;
  197. };
  198. CAsyncOpenedFiles g_AsyncOpenedFiles;
  199. #define FSASYNC_WRITE_PRIORITY JP_LOW
  200. //---------------------------------------------------------
  201. ASSERT_INVARIANT( (int)FSASYNC_OK == (int)JOB_OK );
  202. ASSERT_INVARIANT( (int)FSASYNC_STATUS_PENDING == (int)JOB_STATUS_PENDING )
  203. ASSERT_INVARIANT( (int)FSASYNC_STATUS_INPROGRESS == (int)JOB_STATUS_INPROGRESS );
  204. ASSERT_INVARIANT( (int)FSASYNC_STATUS_ABORTED == (int)JOB_STATUS_ABORTED );
  205. ASSERT_INVARIANT( (int)FSASYNC_STATUS_UNSERVICED == (int)JOB_STATUS_UNSERVICED );
  206. //---------------------------------------------------------
  207. // A standard filesystem job
  208. //---------------------------------------------------------
  209. class CFileAsyncJob : public CJob
  210. {
  211. public:
  212. CFileAsyncJob( JobPriority_t priority = JP_NORMAL )
  213. : CJob( priority )
  214. {
  215. SetFlags( GetFlags() | JF_IO );
  216. }
  217. virtual JobStatus_t GetResult( void **ppData, int *pSize ) { *ppData = NULL; *pSize = 0; return GetStatus(); }
  218. virtual bool IsWrite() const { return false; }
  219. };
  220. //---------------------------------------------------------
  221. // A standard filesystem read job
  222. //---------------------------------------------------------
  223. class CFileAsyncReadJob : public CFileAsyncJob,
  224. protected FileAsyncRequest_t
  225. {
  226. public:
  227. CFileAsyncReadJob( const FileAsyncRequest_t &fromRequest )
  228. : CFileAsyncJob( ConvertPriority( fromRequest.priority ) ),
  229. FileAsyncRequest_t( fromRequest ),
  230. m_pResultData( NULL ),
  231. m_nResultSize( 0 ),
  232. m_pRealContext( fromRequest.pContext ),
  233. m_pfnRealCallback( fromRequest.pfnCallback )
  234. {
  235. #if defined( TRACK_BLOCKING_IO )
  236. m_Timer.Start();
  237. #endif
  238. pszFilename = strdup( fromRequest.pszFilename );
  239. Q_FixSlashes( const_cast<char*>( pszFilename ) );
  240. pContext = this;
  241. pfnCallback = InterceptCallback;
  242. if ( hSpecificAsyncFile != FS_INVALID_ASYNC_FILE )
  243. {
  244. g_AsyncOpenedFiles.AddRef( hSpecificAsyncFile );
  245. }
  246. #if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  247. m_pszAllocCreditFile = NULL;
  248. m_nAllocCreditLine = 0;
  249. #endif
  250. }
  251. ~CFileAsyncReadJob()
  252. {
  253. if ( hSpecificAsyncFile != FS_INVALID_ASYNC_FILE )
  254. {
  255. g_AsyncOpenedFiles.Release( hSpecificAsyncFile );
  256. }
  257. if ( pszFilename )
  258. free( (void *)pszFilename );
  259. }
  260. virtual char const *Describe()
  261. {
  262. return pszFilename;
  263. }
  264. const FileAsyncRequest_t *GetRequest() const
  265. {
  266. return this;
  267. }
  268. virtual JobStatus_t DoExecute()
  269. {
  270. SimulateDelay();
  271. #if defined( TRACK_BLOCKING_IO )
  272. bool oldState = BaseFileSystem()->SetAllowSynchronousLogging( false );
  273. #endif
  274. #if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  275. if ( m_pszAllocCreditFile )
  276. MemAlloc_PushAllocDbgInfo( m_pszAllocCreditFile, m_nAllocCreditLine );
  277. #endif
  278. JobStatus_t retval = BaseFileSystem()->SyncRead( *this );
  279. #if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  280. if ( m_pszAllocCreditFile )
  281. MemAlloc_PopAllocDbgInfo();
  282. #endif
  283. #if defined( TRACK_BLOCKING_IO )
  284. m_Timer.End();
  285. FileBlockingItem item( FILESYSTEM_BLOCKING_ASYNCHRONOUS, Describe(), m_Timer.GetDuration().GetSeconds(), FileBlockingItem::FB_ACCESS_READ );
  286. BaseFileSystem()->RecordBlockingFileAccess( false, item );
  287. BaseFileSystem()->SetAllowSynchronousLogging( oldState );
  288. #endif
  289. return retval;
  290. }
  291. virtual JobStatus_t GetResult( void **ppData, int *pSize )
  292. {
  293. if ( m_pResultData )
  294. {
  295. *ppData = m_pResultData;
  296. *pSize = m_nResultSize;
  297. }
  298. return GetStatus();
  299. }
  300. static void InterceptCallback( const FileAsyncRequest_t &request, int nBytesRead, FSAsyncStatus_t result )
  301. {
  302. CFileAsyncReadJob *pJob = (CFileAsyncReadJob *)request.pContext;
  303. if ( result == FSASYNC_OK && !( request.flags & FSASYNC_FLAGS_FREEDATAPTR ) )
  304. {
  305. pJob->m_pResultData = request.pData;
  306. pJob->m_nResultSize = nBytesRead;
  307. }
  308. if ( pJob->m_pfnRealCallback )
  309. {
  310. // Going to slam the values. Not used after this point. Make temps if that changes
  311. FileAsyncRequest_t &temp = const_cast<FileAsyncRequest_t &>(request);
  312. temp.pfnCallback = pJob->m_pfnRealCallback;
  313. temp.pContext = pJob->m_pRealContext;
  314. (*pJob->m_pfnRealCallback)( temp, nBytesRead, result );
  315. }
  316. }
  317. void SetAllocCredit( const char *pszFile, int line )
  318. {
  319. #if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  320. m_pszAllocCreditFile = pszFile;
  321. m_nAllocCreditLine = line;
  322. #endif
  323. }
  324. private:
  325. void * m_pResultData;
  326. int m_nResultSize;
  327. void * m_pRealContext;
  328. FSAsyncCallbackFunc_t m_pfnRealCallback;
  329. #if defined( TRACK_BLOCKING_IO )
  330. CFastTimer m_Timer;
  331. #endif
  332. #if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  333. const char * m_pszAllocCreditFile;
  334. int m_nAllocCreditLine;
  335. #endif
  336. };
  337. //---------------------------------------------------------
  338. // Append to a file
  339. //---------------------------------------------------------
  340. static int g_nAsyncWriteJobs;
  341. class CFileAsyncWriteJob : public CFileAsyncJob
  342. {
  343. public:
  344. CFileAsyncWriteJob( const char *pszFilename, const void *pData, unsigned nBytes, bool bFreeMemory, bool bAppend )
  345. : CFileAsyncJob( FSASYNC_WRITE_PRIORITY ),
  346. m_pData( pData ),
  347. m_nBytes( nBytes ),
  348. m_bFreeMemory( bFreeMemory ),
  349. m_bAppend( bAppend )
  350. {
  351. #if defined( TRACK_BLOCKING_IO )
  352. m_Timer.Start();
  353. #endif
  354. m_pszFilename = strdup( pszFilename );
  355. g_nAsyncWriteJobs++;
  356. SetFlags( GetFlags() | JF_SERIAL );
  357. }
  358. ~CFileAsyncWriteJob()
  359. {
  360. g_nAsyncWriteJobs--;
  361. free( (void *)m_pszFilename );
  362. }
  363. virtual char const *Describe() { return m_pszFilename; }
  364. virtual bool IsWrite() const { return true; }
  365. virtual JobStatus_t DoExecute()
  366. {
  367. SimulateDelay();
  368. #if defined( TRACK_BLOCKING_IO )
  369. bool oldState = BaseFileSystem()->SetAllowSynchronousLogging( false );
  370. #endif
  371. JobStatus_t retval = BaseFileSystem()->SyncWrite( m_pszFilename, m_pData, m_nBytes, false, m_bAppend );
  372. #if defined( TRACK_BLOCKING_IO )
  373. m_Timer.End();
  374. FileBlockingItem item( FILESYSTEM_BLOCKING_ASYNCHRONOUS, Describe(), m_Timer.GetDuration().GetSeconds(), FileBlockingItem::FB_ACCESS_WRITE );
  375. BaseFileSystem()->RecordBlockingFileAccess( false, item );
  376. BaseFileSystem()->SetAllowSynchronousLogging( oldState );
  377. #endif
  378. return retval;
  379. }
  380. virtual void DoCleanup()
  381. {
  382. if ( m_pData && m_bFreeMemory )
  383. {
  384. delete (char*)m_pData;
  385. }
  386. }
  387. protected:
  388. bool m_bFreeMemory;
  389. private:
  390. const char *m_pszFilename;
  391. const void *m_pData;
  392. int m_nBytes;
  393. bool m_bAppend;
  394. #if defined( TRACK_BLOCKING_IO )
  395. CFastTimer m_Timer;
  396. #endif
  397. };
  398. class CFileAsyncWriteFileJob : public CFileAsyncWriteJob
  399. {
  400. public:
  401. CFileAsyncWriteFileJob( const char *pszFilename, const CUtlBuffer *pData, unsigned nBytes, bool bFreeMemory, bool bAppend )
  402. : CFileAsyncWriteJob( pszFilename, pData->Base(), nBytes, bFreeMemory, bAppend ),
  403. m_pBuffer( pData )
  404. {
  405. }
  406. virtual void DoCleanup()
  407. {
  408. if ( m_pBuffer && m_bFreeMemory )
  409. {
  410. delete m_pBuffer;
  411. }
  412. }
  413. private:
  414. const CUtlBuffer *m_pBuffer;
  415. };
  416. //---------------------------------------------------------
  417. // Append two files
  418. //---------------------------------------------------------
  419. class CFileAsyncAppendFileJob : public CFileAsyncJob
  420. {
  421. public:
  422. CFileAsyncAppendFileJob( const char *pszAppendTo, const char *pszAppendFrom )
  423. : CFileAsyncJob( FSASYNC_WRITE_PRIORITY )
  424. {
  425. #if defined( TRACK_BLOCKING_IO )
  426. m_Timer.Start();
  427. #endif
  428. m_pszAppendTo = strdup( pszAppendTo );
  429. m_pszAppendFrom = strdup( pszAppendFrom );
  430. Q_FixSlashes( const_cast<char*>( m_pszAppendTo ) );
  431. Q_FixSlashes( const_cast<char*>( m_pszAppendFrom ) );
  432. g_nAsyncWriteJobs++;
  433. SetFlags( GetFlags() | JF_SERIAL );
  434. }
  435. ~CFileAsyncAppendFileJob()
  436. {
  437. g_nAsyncWriteJobs--;
  438. }
  439. virtual char const *Describe() { return m_pszAppendTo; }
  440. virtual bool IsWrite() const { return true; }
  441. virtual JobStatus_t DoExecute()
  442. {
  443. SimulateDelay();
  444. #if defined( TRACK_BLOCKING_IO )
  445. bool oldState = BaseFileSystem()->SetAllowSynchronousLogging( false );
  446. #endif
  447. JobStatus_t retval = BaseFileSystem()->SyncAppendFile( m_pszAppendTo, m_pszAppendFrom );
  448. #if defined( TRACK_BLOCKING_IO )
  449. m_Timer.End();
  450. FileBlockingItem item( FILESYSTEM_BLOCKING_ASYNCHRONOUS, Describe(), m_Timer.GetDuration().GetSeconds(), FileBlockingItem::FB_ACCESS_APPEND );
  451. BaseFileSystem()->RecordBlockingFileAccess( false, item );
  452. BaseFileSystem()->SetAllowSynchronousLogging( oldState );
  453. #endif
  454. return retval;
  455. }
  456. private:
  457. const char *m_pszAppendTo;
  458. const char *m_pszAppendFrom;
  459. #if defined( TRACK_BLOCKING_IO )
  460. CFastTimer m_Timer;
  461. #endif
  462. };
  463. //---------------------------------------------------------
  464. // Job to find out file size
  465. //---------------------------------------------------------
  466. class CFileAsyncFileSizeJob : public CFileAsyncReadJob
  467. {
  468. public:
  469. CFileAsyncFileSizeJob( const FileAsyncRequest_t &fromRequest )
  470. : CFileAsyncReadJob( fromRequest )
  471. {
  472. #if defined( TRACK_BLOCKING_IO )
  473. m_Timer.Start();
  474. #endif
  475. }
  476. virtual JobStatus_t DoExecute()
  477. {
  478. SimulateDelay();
  479. #if defined( TRACK_BLOCKING_IO )
  480. bool oldState = BaseFileSystem()->SetAllowSynchronousLogging( false );
  481. #endif
  482. JobStatus_t retval = BaseFileSystem()->SyncGetFileSize( *this );
  483. #if defined( TRACK_BLOCKING_IO )
  484. m_Timer.End();
  485. FileBlockingItem item( FILESYSTEM_BLOCKING_ASYNCHRONOUS, Describe(), m_Timer.GetDuration().GetSeconds(), FileBlockingItem::FB_ACCESS_SIZE );
  486. BaseFileSystem()->RecordBlockingFileAccess( false, item );
  487. BaseFileSystem()->SetAllowSynchronousLogging( oldState );
  488. #endif
  489. return retval;
  490. }
  491. #if defined( TRACK_BLOCKING_IO )
  492. private:
  493. CFastTimer m_Timer;
  494. #endif
  495. };
  496. //-----------------------------------------------------------------------------
  497. //
  498. //-----------------------------------------------------------------------------
  499. void CBaseFileSystem::InitAsync()
  500. {
  501. Assert( !m_pThreadPool );
  502. if ( m_pThreadPool )
  503. return;
  504. #ifndef OSX
  505. if ( IsX360() && Plat_IsInDebugSession() )
  506. {
  507. class CBreakThread : public CThread
  508. {
  509. virtual int Run()
  510. {
  511. for (;;)
  512. {
  513. Sleep(1000);
  514. static int wakeCount;
  515. wakeCount++;
  516. volatile static int bForceResume = false;
  517. if ( bForceResume )
  518. {
  519. bForceResume = false;
  520. BaseFileSystem()->AsyncResume();
  521. }
  522. }
  523. }
  524. };
  525. static CBreakThread breakThread;
  526. breakThread.SetName( "DebugBreakThread" );
  527. breakThread.Start( 1024 );
  528. }
  529. #endif
  530. if ( CommandLine()->FindParm( "-noasync" ) )
  531. {
  532. Msg( "Async I/O disabled from command line\n" );
  533. return;
  534. }
  535. if ( VCRGetMode() == VCR_Disabled )
  536. {
  537. // create the i/o thread pool
  538. m_pThreadPool = CreateNewThreadPool();
  539. ThreadPoolStartParams_t params;
  540. params.iThreadPriority = 0;
  541. params.bIOThreads = true;
  542. if ( IsX360() )
  543. {
  544. // override defaults
  545. // 360 has a single i/o thread on the farthest proc
  546. params.nThreads = 1;
  547. params.fDistribute = TRS_TRUE;
  548. params.bUseAffinityTable = true;
  549. params.iAffinityTable[0] = XBOX_PROCESSOR_3;
  550. }
  551. else
  552. {
  553. params.nThreads = MIN( params.nThreads, 4 ); // > 4 threads doing IO on one drive, are you crazy?
  554. params.nStackSize = 256*1024;
  555. }
  556. if ( !m_pThreadPool->Start( params, "FsAsyncIO" ) )
  557. {
  558. SafeRelease( m_pThreadPool );
  559. }
  560. }
  561. }
  562. //-----------------------------------------------------------------------------
  563. //
  564. //-----------------------------------------------------------------------------
  565. void CBaseFileSystem::ShutdownAsync()
  566. {
  567. if ( m_pThreadPool )
  568. {
  569. AsyncFlush();
  570. m_pThreadPool->Stop();
  571. SafeRelease( m_pThreadPool );
  572. }
  573. }
  574. //-----------------------------------------------------------------------------
  575. //
  576. //-----------------------------------------------------------------------------
  577. FSAsyncStatus_t CBaseFileSystem::AsyncReadMultiple( const FileAsyncRequest_t *pRequests, int nRequests, FSAsyncControl_t *phControls )
  578. {
  579. return AsyncReadMultipleCreditAlloc( pRequests, nRequests, NULL, 0, phControls );
  580. }
  581. //-----------------------------------------------------------------------------
  582. //
  583. //-----------------------------------------------------------------------------
  584. FSAsyncStatus_t CBaseFileSystem::AsyncReadMultipleCreditAlloc( const FileAsyncRequest_t *pRequests, int nRequests, const char *pszFile, int line, FSAsyncControl_t *phControls )
  585. {
  586. bool bAsyncMode = ( GetAsyncMode() == FSAM_ASYNC );
  587. bool bSynchronous = ( !bAsyncMode || ( pRequests[0].flags & FSASYNC_FLAGS_SYNC ) || !m_pThreadPool );
  588. if ( !bAsyncMode )
  589. {
  590. AsyncFinishAll();
  591. }
  592. CFileAsyncReadJob *pJob;
  593. for ( int i = 0; i < nRequests; i++ )
  594. {
  595. if ( pRequests[i].nBytes >= 0 )
  596. {
  597. pJob = new CFileAsyncReadJob( pRequests[i] );
  598. }
  599. else
  600. {
  601. pJob = new CFileAsyncFileSizeJob( pRequests[i] );
  602. }
  603. #if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  604. pJob->SetAllocCredit( pszFile, line );
  605. #endif
  606. if ( !bSynchronous )
  607. {
  608. // async mode, queue request
  609. m_pThreadPool->AddJob( pJob );
  610. }
  611. else
  612. {
  613. // synchronous mode, execute now
  614. pJob->Execute();
  615. }
  616. if ( phControls )
  617. {
  618. phControls[i] = (FSAsyncControl_t)pJob;
  619. }
  620. else
  621. {
  622. pJob->Release();
  623. }
  624. }
  625. return FSASYNC_OK;
  626. }
  627. //-----------------------------------------------------------------------------
  628. //
  629. //-----------------------------------------------------------------------------
  630. FSAsyncStatus_t CBaseFileSystem::AsyncWrite(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl )
  631. {
  632. bool bAsyncMode = ( GetAsyncMode() == FSAM_ASYNC );
  633. bool bSynchronous = ( !bAsyncMode || !m_pThreadPool );
  634. if ( !bAsyncMode )
  635. {
  636. AsyncFinishAll();
  637. }
  638. CJob *pJob = new CFileAsyncWriteJob( pFileName, pSrc, nSrcBytes, bFreeMemory, bAppend );
  639. if ( !bSynchronous )
  640. {
  641. m_pThreadPool->AddJob( pJob );
  642. }
  643. else
  644. {
  645. pJob->Execute();
  646. }
  647. if ( pControl )
  648. {
  649. *pControl = (FSAsyncControl_t)pJob;
  650. }
  651. else
  652. {
  653. pJob->Release();
  654. }
  655. return FSASYNC_OK;
  656. }
  657. //-----------------------------------------------------------------------------
  658. //
  659. //-----------------------------------------------------------------------------
  660. FSAsyncStatus_t CBaseFileSystem::AsyncWriteFile(const char *pFileName, const CUtlBuffer *pBuff, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl )
  661. {
  662. bool bAsyncMode = ( GetAsyncMode() == FSAM_ASYNC );
  663. bool bSynchronous = ( !bAsyncMode || !m_pThreadPool );
  664. if ( !bAsyncMode )
  665. {
  666. AsyncFinishAll();
  667. }
  668. CJob *pJob = new CFileAsyncWriteFileJob( pFileName, pBuff, nSrcBytes, bFreeMemory, bAppend );
  669. if ( !bSynchronous )
  670. {
  671. m_pThreadPool->AddJob( pJob );
  672. }
  673. else
  674. {
  675. pJob->Execute();
  676. }
  677. if ( pControl )
  678. {
  679. *pControl = (FSAsyncControl_t)pJob;
  680. }
  681. else
  682. {
  683. pJob->Release();
  684. }
  685. return FSASYNC_OK;
  686. }
  687. //-----------------------------------------------------------------------------
  688. //
  689. //-----------------------------------------------------------------------------
  690. FSAsyncStatus_t CBaseFileSystem::AsyncAppendFile(const char *pAppendToFileName, const char *pAppendFromFileName, FSAsyncControl_t *pControl )
  691. {
  692. bool bAsyncMode = ( GetAsyncMode() == FSAM_ASYNC );
  693. bool bSynchronous = ( !bAsyncMode || !m_pThreadPool );
  694. if ( !bAsyncMode )
  695. {
  696. AsyncFinishAll();
  697. }
  698. CJob *pJob = new CFileAsyncAppendFileJob( pAppendToFileName, pAppendFromFileName );
  699. if ( !bSynchronous )
  700. {
  701. m_pThreadPool->AddJob( pJob );
  702. }
  703. else
  704. {
  705. pJob->Execute();
  706. }
  707. if ( pControl )
  708. {
  709. *pControl = (FSAsyncControl_t)pJob;
  710. }
  711. else
  712. {
  713. pJob->Release();
  714. }
  715. return FSASYNC_OK;
  716. }
  717. //-----------------------------------------------------------------------------
  718. //
  719. //-----------------------------------------------------------------------------
  720. CThreadMutex g_AsyncFinishMutex;
  721. void CBaseFileSystem::AsyncFinishAll( int iToPriority )
  722. {
  723. if ( m_pThreadPool)
  724. {
  725. AUTO_LOCK( g_AsyncFinishMutex );
  726. m_pThreadPool->ExecuteToPriority( ConvertPriority( iToPriority ) );
  727. }
  728. }
  729. //-----------------------------------------------------------------------------
  730. //
  731. //-----------------------------------------------------------------------------
  732. static bool AsyncWriteJobFilter( CJob *pJob )
  733. {
  734. CFileAsyncJob *pFileJob = dynamic_cast<CFileAsyncJob *>(pJob);
  735. return ( pFileJob && pFileJob->IsWrite() );
  736. }
  737. void CBaseFileSystem::AsyncFinishAllWrites()
  738. {
  739. if ( m_pThreadPool && g_nAsyncWriteJobs )
  740. {
  741. AUTO_LOCK( g_AsyncFinishMutex );
  742. m_pThreadPool->ExecuteAll( AsyncWriteJobFilter );
  743. }
  744. }
  745. //-----------------------------------------------------------------------------
  746. //
  747. //-----------------------------------------------------------------------------
  748. bool CBaseFileSystem::AsyncSuspend()
  749. {
  750. if ( m_pThreadPool )
  751. {
  752. m_pThreadPool->SuspendExecution();
  753. }
  754. return true;
  755. }
  756. //-----------------------------------------------------------------------------
  757. //
  758. //-----------------------------------------------------------------------------
  759. bool CBaseFileSystem::AsyncResume()
  760. {
  761. if ( m_pThreadPool )
  762. {
  763. m_pThreadPool->ResumeExecution();
  764. }
  765. return true;
  766. }
  767. //-----------------------------------------------------------------------------
  768. //
  769. //-----------------------------------------------------------------------------
  770. FSAsyncStatus_t CBaseFileSystem::AsyncBeginRead( const char *pszFile, FSAsyncFile_t *phFile )
  771. {
  772. #if !(defined(FILESYSTEM_STEAM) || defined(DEDICATED))
  773. if ( AsyncAllowHeldFiles() )
  774. {
  775. *phFile = g_AsyncOpenedFiles.FindOrAdd( pszFile );
  776. return FSASYNC_OK;
  777. }
  778. #endif
  779. *phFile = FS_INVALID_ASYNC_FILE;
  780. return FSASYNC_OK;
  781. }
  782. //-----------------------------------------------------------------------------
  783. //
  784. //-----------------------------------------------------------------------------
  785. FSAsyncStatus_t CBaseFileSystem::AsyncEndRead( FSAsyncFile_t hFile )
  786. {
  787. #if !(defined(FILESYSTEM_STEAM) || defined(DEDICATED))
  788. if ( hFile != FS_INVALID_ASYNC_FILE )
  789. g_AsyncOpenedFiles.Release( hFile );
  790. #endif
  791. return FSASYNC_OK;
  792. }
  793. //-----------------------------------------------------------------------------
  794. //
  795. //-----------------------------------------------------------------------------
  796. FSAsyncStatus_t CBaseFileSystem::AsyncFinish( FSAsyncControl_t hControl, bool wait )
  797. {
  798. if ( wait )
  799. {
  800. CJob *pJob = (CJob *)hControl;
  801. if ( !pJob )
  802. {
  803. return FSASYNC_ERR_FAILURE;
  804. }
  805. #if defined( TRACK_BLOCKING_IO )
  806. CFastTimer timer;
  807. timer.Start();
  808. BaseFileSystem()->SetAllowSynchronousLogging( false );
  809. #endif
  810. FSAsyncStatus_t retval = (FSAsyncStatus_t)pJob->Execute();
  811. #if defined( TRACK_BLOCKING_IO )
  812. timer.End();
  813. FileBlockingItem item( FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK, pJob->Describe(), timer.GetDuration().GetSeconds(), FileBlockingItem::FB_ACCESS_READ );
  814. BaseFileSystem()->RecordBlockingFileAccess( false, item );
  815. BaseFileSystem()->SetAllowSynchronousLogging( true );
  816. #endif
  817. return retval;
  818. }
  819. AsyncSetPriority( hControl, INT_MAX );
  820. return FSASYNC_OK;
  821. }
  822. //-----------------------------------------------------------------------------
  823. //
  824. //-----------------------------------------------------------------------------
  825. FSAsyncStatus_t CBaseFileSystem::AsyncGetResult( FSAsyncControl_t hControl, void **ppData, int *pSize )
  826. {
  827. if ( ppData )
  828. {
  829. *ppData = NULL;
  830. }
  831. if ( pSize )
  832. {
  833. *pSize = 0;
  834. }
  835. CFileAsyncJob *pJob = (CFileAsyncJob *)hControl;
  836. if ( !pJob )
  837. {
  838. return FSASYNC_ERR_FAILURE;
  839. }
  840. if ( pJob->IsFinished() )
  841. {
  842. return (FSAsyncStatus_t)pJob->GetResult( ppData, pSize );
  843. }
  844. return FSASYNC_STATUS_PENDING;
  845. }
  846. //-----------------------------------------------------------------------------
  847. //
  848. //-----------------------------------------------------------------------------
  849. FSAsyncStatus_t CBaseFileSystem::AsyncAbort( FSAsyncControl_t hControl )
  850. {
  851. CJob *pJob = (CJob *)hControl;
  852. if ( !pJob )
  853. {
  854. return FSASYNC_ERR_FAILURE;
  855. }
  856. return (FSAsyncStatus_t)pJob->Abort();
  857. }
  858. //-----------------------------------------------------------------------------
  859. //
  860. //-----------------------------------------------------------------------------
  861. FSAsyncStatus_t CBaseFileSystem::AsyncStatus( FSAsyncControl_t hControl )
  862. {
  863. CJob *pJob = (CJob *)hControl;
  864. if ( !pJob )
  865. {
  866. return FSASYNC_ERR_FAILURE;
  867. }
  868. return (FSAsyncStatus_t)pJob->GetStatus();
  869. }
  870. //-----------------------------------------------------------------------------
  871. //
  872. //-----------------------------------------------------------------------------
  873. FSAsyncStatus_t CBaseFileSystem::AsyncFlush()
  874. {
  875. if ( m_pThreadPool )
  876. {
  877. m_pThreadPool->AbortAll();
  878. }
  879. return FSASYNC_OK;
  880. }
  881. //-----------------------------------------------------------------------------
  882. //
  883. //-----------------------------------------------------------------------------
  884. FSAsyncStatus_t CBaseFileSystem::AsyncSetPriority(FSAsyncControl_t hControl, int newPriority)
  885. {
  886. if ( m_pThreadPool )
  887. {
  888. CJob *pJob = (CJob *)hControl;
  889. if ( !pJob )
  890. {
  891. return FSASYNC_ERR_FAILURE;
  892. }
  893. JobPriority_t internalPriority = ConvertPriority( newPriority );
  894. if ( internalPriority != pJob->GetPriority() )
  895. {
  896. m_pThreadPool->ChangePriority( pJob, internalPriority );
  897. }
  898. }
  899. return FSASYNC_OK;
  900. }
  901. //-----------------------------------------------------------------------------
  902. //
  903. //-----------------------------------------------------------------------------
  904. void CBaseFileSystem::AsyncAddRef( FSAsyncControl_t hControl )
  905. {
  906. CJob *pJob = (CJob *)hControl;
  907. if ( pJob )
  908. {
  909. pJob->AddRef();
  910. }
  911. }
  912. //-----------------------------------------------------------------------------
  913. //
  914. //-----------------------------------------------------------------------------
  915. void CBaseFileSystem::AsyncRelease( FSAsyncControl_t hControl )
  916. {
  917. CJob *pJob = (CJob *)hControl;
  918. if ( pJob )
  919. {
  920. pJob->Release();
  921. }
  922. }
  923. //-----------------------------------------------------------------------------
  924. //
  925. //-----------------------------------------------------------------------------
  926. static void *GetDest( const FileAsyncRequest_t &request, bool bTryUnbuffered, unsigned *pBytesBuffer, unsigned *pBytesRead )
  927. {
  928. }
  929. //-----------------------------------------------------------------------------
  930. //
  931. //-----------------------------------------------------------------------------
  932. FSAsyncStatus_t CBaseFileSystem::SyncRead( const FileAsyncRequest_t &request )
  933. {
  934. Assert( request.nBytes >=0 );
  935. if ( request.nBytes < 0 || request.nOffset < 0 )
  936. {
  937. Msg( "Invalid async read of %s\n", request.pszFilename );
  938. DoAsyncCallback( request, NULL, 0, FSASYNC_ERR_FILEOPEN );
  939. return FSASYNC_ERR_FILEOPEN;
  940. }
  941. FSAsyncStatus_t result;
  942. AsyncOpenedFile_t *pHeldFile = ( request.hSpecificAsyncFile != FS_INVALID_ASYNC_FILE ) ? g_AsyncOpenedFiles.Get( request.hSpecificAsyncFile ) : NULL;
  943. FileHandle_t hFile;
  944. if ( !pHeldFile || pHeldFile->hFile == FILESYSTEM_INVALID_HANDLE )
  945. {
  946. hFile = OpenEx( request.pszFilename, "rb", 0, request.pszPathID );
  947. if ( pHeldFile )
  948. {
  949. pHeldFile->hFile = hFile;
  950. }
  951. }
  952. else
  953. {
  954. hFile = pHeldFile->hFile;
  955. }
  956. if ( hFile )
  957. {
  958. // ------------------------------------------------------
  959. int nBytesToRead = ( request.nBytes ) ? request.nBytes : Size( hFile ) - request.nOffset;
  960. int nBytesBuffer;
  961. void *pDest;
  962. if ( nBytesToRead < 0 )
  963. {
  964. nBytesToRead = 0; // bad offset?
  965. }
  966. if ( request.pData )
  967. {
  968. // caller provided buffer
  969. Assert( !( request.flags & FSASYNC_FLAGS_NULLTERMINATE ) );
  970. pDest = request.pData;
  971. nBytesBuffer = nBytesToRead;
  972. }
  973. else
  974. {
  975. // allocate an optimal buffer
  976. unsigned nOffsetAlign;
  977. nBytesBuffer = nBytesToRead + ( ( request.flags & FSASYNC_FLAGS_NULLTERMINATE ) ? 1 : 0 );
  978. if ( GetOptimalIOConstraints( hFile, &nOffsetAlign, NULL, NULL) && ( request.nOffset % nOffsetAlign == 0 ) )
  979. {
  980. nBytesBuffer = GetOptimalReadSize( hFile, nBytesBuffer );
  981. }
  982. if ( !request.pfnAlloc )
  983. {
  984. pDest = AllocOptimalReadBuffer( hFile, nBytesBuffer, request.nOffset );
  985. }
  986. else
  987. {
  988. pDest = (*request.pfnAlloc)( request.pszFilename, nBytesBuffer );
  989. }
  990. }
  991. SetBufferSize( hFile, 0 ); // TODO: what if it's a pack file? restore buffer size?
  992. if ( request.nOffset > 0 )
  993. {
  994. Seek( hFile, request.nOffset, FILESYSTEM_SEEK_HEAD );
  995. }
  996. // perform the read operation
  997. int nBytesRead = ReadEx( pDest, nBytesBuffer, nBytesToRead, hFile );
  998. if ( !pHeldFile )
  999. {
  1000. Close( hFile );
  1001. }
  1002. if ( request.flags & FSASYNC_FLAGS_NULLTERMINATE )
  1003. {
  1004. ((char *)pDest)[nBytesRead] = 0;
  1005. }
  1006. result = ( ( nBytesRead == 0 ) && ( nBytesToRead != 0 ) ) ? FSASYNC_ERR_READING : FSASYNC_OK;
  1007. DoAsyncCallback( request, pDest, MIN( nBytesRead, nBytesToRead ), result );
  1008. }
  1009. else
  1010. {
  1011. DoAsyncCallback( request, NULL, 0, FSASYNC_ERR_FILEOPEN );
  1012. result = FSASYNC_ERR_FILEOPEN;
  1013. }
  1014. if ( pHeldFile )
  1015. {
  1016. g_AsyncOpenedFiles.Release( request.hSpecificAsyncFile );
  1017. }
  1018. if ( m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC )
  1019. {
  1020. LogAccessToFile( "async", request.pszFilename, "" );
  1021. }
  1022. return result;
  1023. }
  1024. //-----------------------------------------------------------------------------
  1025. //
  1026. //-----------------------------------------------------------------------------
  1027. FSAsyncStatus_t CBaseFileSystem::SyncGetFileSize( const FileAsyncRequest_t &request )
  1028. {
  1029. int size = Size( request.pszFilename, request.pszPathID );
  1030. DoAsyncCallback( request, NULL, size, ( size ) ? FSASYNC_OK : FSASYNC_ERR_FILEOPEN );
  1031. return FSASYNC_OK;
  1032. }
  1033. //-----------------------------------------------------------------------------
  1034. //
  1035. //-----------------------------------------------------------------------------
  1036. FSAsyncStatus_t CBaseFileSystem::SyncWrite(const char *pszFilename, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend )
  1037. {
  1038. FileHandle_t hFile = OpenEx( pszFilename, ( bAppend ) ? "ab+" : "wb", IsX360() ? FSOPEN_NEVERINPACK : 0, NULL );
  1039. if ( hFile )
  1040. {
  1041. SetBufferSize( hFile, 0 );
  1042. Write( pSrc, nSrcBytes, hFile );
  1043. Close( hFile );
  1044. if ( bFreeMemory )
  1045. {
  1046. delete (char*)pSrc;
  1047. }
  1048. if ( m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC )
  1049. {
  1050. LogAccessToFile( "asyncwrite", pszFilename, "" );
  1051. }
  1052. return FSASYNC_OK;
  1053. }
  1054. return FSASYNC_ERR_FILEOPEN;
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. //
  1058. //-----------------------------------------------------------------------------
  1059. FSAsyncStatus_t CBaseFileSystem::SyncAppendFile(const char *pAppendToFileName, const char *pAppendFromFileName )
  1060. {
  1061. FileHandle_t hDestFile = OpenEx( pAppendToFileName, "ab+", IsX360() ? FSOPEN_NEVERINPACK : 0, NULL );
  1062. FSAsyncStatus_t result = FSASYNC_ERR_FAILURE;
  1063. if ( hDestFile )
  1064. {
  1065. SetBufferSize( hDestFile, 0 );
  1066. FileHandle_t hSourceFile = OpenEx( pAppendFromFileName, "rb", IsX360() ? FSOPEN_NEVERINPACK : 0, NULL );
  1067. if ( hSourceFile )
  1068. {
  1069. SetBufferSize( hSourceFile, 0 );
  1070. const int BUFSIZE = 128 * 1024;
  1071. int fileSize = Size( hSourceFile );
  1072. char *buf = (char *)malloc( BUFSIZE );
  1073. int size;
  1074. while ( fileSize > 0 )
  1075. {
  1076. if ( fileSize > BUFSIZE )
  1077. size = BUFSIZE;
  1078. else
  1079. size = fileSize;
  1080. Read( buf, size, hSourceFile );
  1081. Write( buf, size, hDestFile );
  1082. fileSize -= size;
  1083. }
  1084. free(buf);
  1085. Close( hSourceFile );
  1086. result = FSASYNC_OK;
  1087. }
  1088. Close( hDestFile );
  1089. }
  1090. if ( m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC )
  1091. {
  1092. LogAccessToFile( "asyncappend", pAppendToFileName, "" );
  1093. }
  1094. return result;
  1095. }
  1096. //-----------------------------------------------------------------------------
  1097. //
  1098. //-----------------------------------------------------------------------------
  1099. void CBaseFileSystem::DoAsyncCallback( const FileAsyncRequest_t &request, void *pData, int nBytesRead, FSAsyncStatus_t result )
  1100. {
  1101. void *pDataToFree = NULL;
  1102. if ( request.pfnCallback )
  1103. {
  1104. AUTO_LOCK_FM( m_AsyncCallbackMutex );
  1105. if ( pData && request.pData != pData )
  1106. {
  1107. // Allocated the data here
  1108. FileAsyncRequest_t temp = request;
  1109. temp.pData = pData;
  1110. {
  1111. AUTOBLOCKREPORTER_FN( DoAsyncCallback, this, false, temp.pszFilename, FILESYSTEM_BLOCKING_CALLBACKTIMING, FileBlockingItem::FB_ACCESS_READ );
  1112. (*request.pfnCallback)( temp, nBytesRead, result );
  1113. }
  1114. if ( !( request.flags & FSASYNC_FLAGS_ALLOCNOFREE ) )
  1115. {
  1116. pDataToFree = pData;
  1117. }
  1118. }
  1119. else
  1120. {
  1121. {
  1122. AUTOBLOCKREPORTER_FN( DoAsyncCallback, this, false, request.pszFilename, FILESYSTEM_BLOCKING_CALLBACKTIMING, FileBlockingItem::FB_ACCESS_READ );
  1123. (*request.pfnCallback)( request, nBytesRead, result );
  1124. }
  1125. if ( ( request.flags & FSASYNC_FLAGS_FREEDATAPTR ) )
  1126. {
  1127. pDataToFree = request.pData;
  1128. }
  1129. }
  1130. }
  1131. if ( pDataToFree )
  1132. {
  1133. Assert( !request.pfnAlloc );
  1134. #ifdef OSX
  1135. // The ugly delete[] (void*) method generates a compile warning on osx, as it should.
  1136. free( pDataToFree );
  1137. #else
  1138. delete [] pDataToFree;
  1139. #endif
  1140. }
  1141. }