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.

1538 lines
39 KiB

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