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.

1481 lines
37 KiB

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