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.

452 lines
12 KiB

  1. //-----------------------------------------------------------------------------
  2. // NOTE! This should never be called directly from leaf code
  3. // Just use new,delete,malloc,free etc. They will call into this eventually
  4. //-----------------------------------------------------------------------------
  5. #include "pch_tier0.h"
  6. #if IS_WINDOWS_PC
  7. #define WIN_32_LEAN_AND_MEAN
  8. #include <windows.h>
  9. #define VA_COMMIT_FLAGS MEM_COMMIT
  10. #define VA_RESERVE_FLAGS MEM_RESERVE
  11. #elif defined( _X360 )
  12. #undef Verify
  13. #define _XBOX
  14. #include <xtl.h>
  15. #undef _XBOX
  16. #include "xbox/xbox_win32stubs.h"
  17. #define VA_COMMIT_FLAGS (MEM_COMMIT|MEM_NOZERO|MEM_LARGE_PAGES)
  18. #define VA_RESERVE_FLAGS (MEM_RESERVE|MEM_LARGE_PAGES)
  19. #elif defined( _PS3 )
  20. #include "sys/memory.h"
  21. #include "sys/mempool.h"
  22. #include "sys/process.h"
  23. #include <sys/vm.h>
  24. #endif
  25. //#include <malloc.h>
  26. #include <algorithm>
  27. #include "tier0/dbg.h"
  28. #include "tier0/memalloc.h"
  29. #include "tier0/threadtools.h"
  30. #include "tier0/tslist.h"
  31. #include "mem_helpers.h"
  32. #ifndef _PS3
  33. #pragma pack(4)
  34. #endif
  35. #define MIN_SBH_BLOCK 8
  36. #define MIN_SBH_ALIGN 8
  37. #define MAX_SBH_BLOCK 2048
  38. #define MAX_POOL_REGION (4*1024*1024)
  39. #define NUM_POOLS 42
  40. #if defined( _WIN32 ) || defined( _PS3 )
  41. // FIXME: Disable small block heap on win64 for now; it's busted because
  42. // it's expecting SLIST_HEADER to look different than it does on win64
  43. #if !defined( PLATFORM_WINDOWS_PC64 )
  44. #define MEM_SBH_ENABLED 1
  45. #endif
  46. #endif
  47. #if !defined(_CERT) && ( defined(_X360) || defined(_PS3) )
  48. #define TRACK_SBH_COUNTS
  49. #endif
  50. #if defined(_X360)
  51. // 360 uses a 48MB primary (physical) SBH and 10MB secondary (virtual) SBH, with no fallback
  52. #define MBYTES_PRIMARY_SBH 48
  53. #define MEMALLOC_USE_SECONDARY_SBH
  54. #define MBYTES_SECONDARY_SBH 10
  55. #define MEMALLOC_NO_FALLBACK
  56. #elif defined(_PS3)
  57. // PS3 uses just a 32MB SBH - this was enough to avoid overflow when Portal 2 shipped.
  58. // NOTE: when Steam uses the game's tier0 allocator (see memalloc.h), we increase the size
  59. // of the SBH and MBH (see memstd.cpp) to accommodate those extra allocations.
  60. #define MBYTES_PRIMARY_SBH ( 32 + MBYTES_STEAM_SBH_USAGE )
  61. #define MEMALLOC_NO_FALLBACK
  62. #else // _X360 | _PS3
  63. // Other platforms use a 48MB primary SBH and a (32MB) fallback SBH
  64. #define MBYTES_PRIMARY_SBH 48
  65. #endif // _X360 | _PS3
  66. #define MEMSTD_COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;}
  67. //-----------------------------------------------------------------------------
  68. // Small block pool
  69. //-----------------------------------------------------------------------------
  70. class CFreeList : public CTSListBase
  71. {
  72. public:
  73. void Push( void *p ) { CTSListBase::Push( (TSLNodeBase_t *)p ); }
  74. byte *Pop() { return (byte *)CTSListBase::Pop(); }
  75. };
  76. template <typename CAllocator>
  77. class CSmallBlockHeap;
  78. template <typename CAllocator>
  79. class CSmallBlockPool
  80. {
  81. public:
  82. CSmallBlockPool()
  83. {
  84. m_nBlockSize = 0;
  85. m_nCommittedPages = 0;
  86. m_pFirstPage = NULL;
  87. }
  88. void Init( unsigned nBlockSize );
  89. size_t GetBlockSize();
  90. void *Alloc();
  91. void Free( void *p );
  92. int CountFreeBlocks();
  93. int GetCommittedSize();
  94. int CountCommittedBlocks();
  95. int CountAllocatedBlocks();
  96. size_t Compact( bool bIncremental );
  97. bool Validate();
  98. enum
  99. {
  100. BYTES_PAGE = CAllocator::BYTES_PAGE,
  101. NOT_COMMITTED = -1
  102. };
  103. private:
  104. typedef CSmallBlockHeap<CAllocator> CHeap;
  105. friend class CSmallBlockHeap<CAllocator>;
  106. struct PageStatus_t : public TSLNodeBase_t
  107. {
  108. PageStatus_t()
  109. {
  110. m_pPool = NULL;
  111. m_nAllocated = NOT_COMMITTED;
  112. m_pNextPageInPool = NULL;
  113. }
  114. CSmallBlockPool<CAllocator> * m_pPool;
  115. PageStatus_t * m_pNextPageInPool;
  116. CInterlockedInt m_nAllocated;
  117. CTSListBase m_SortList;
  118. };
  119. struct SharedData_t
  120. {
  121. CAllocator m_Allocator;
  122. CTSListBase m_FreePages;
  123. CThreadSpinRWLock m_Lock;
  124. PageStatus_t m_PageStatus[CAllocator::TOTAL_BYTES/CAllocator::BYTES_PAGE];
  125. byte * m_pNextBlock;
  126. byte * m_pBase;
  127. byte * m_pLimit;
  128. };
  129. static int PageSort( const void *p1, const void *p2 ) ;
  130. bool RemovePagesFromFreeList( byte **pPages, int nPages, bool bSortList );
  131. void ValidateFreelist( SharedData_t *pSharedData );
  132. CFreeList m_FreeList;
  133. CInterlockedPtr<byte> m_pNextAlloc;
  134. PageStatus_t * m_pFirstPage;
  135. unsigned m_nBlockSize;
  136. unsigned m_nCommittedPages;
  137. CThreadFastMutex m_CommitMutex;
  138. #ifdef TRACK_SBH_COUNTS
  139. CInterlockedInt m_nFreeBlocks;
  140. #endif
  141. static SharedData_t *GetSharedData()
  142. {
  143. return &gm_SharedData;
  144. }
  145. static SharedData_t gm_SharedData;
  146. };
  147. //-----------------------------------------------------------------------------
  148. // Small block heap (multi-pool)
  149. //-----------------------------------------------------------------------------
  150. template <typename CAllocator>
  151. class CSmallBlockHeap
  152. {
  153. public:
  154. CSmallBlockHeap();
  155. bool ShouldUse( size_t nBytes );
  156. bool IsOwner( void * p );
  157. void *Alloc( size_t nBytes );
  158. void *Realloc( void *p, size_t nBytes );
  159. void Free( void *p );
  160. size_t GetSize( void *p );
  161. void DumpStats( const char *pszTag, FILE *pFile = NULL );
  162. void Usage( size_t &bytesCommitted, size_t &bytesAllocated );
  163. size_t Compact( bool bIncremental );
  164. bool Validate();
  165. enum
  166. {
  167. BYTES_PAGE = CAllocator::BYTES_PAGE
  168. };
  169. private:
  170. typedef CSmallBlockPool<CAllocator> CPool;
  171. typedef struct CSmallBlockPool<CAllocator>::SharedData_t SharedData_t;
  172. CPool *FindPool( size_t nBytes );
  173. CPool *FindPool( void *p );
  174. // Map size to a pool address to a pool
  175. CPool *m_PoolLookup[MAX_SBH_BLOCK >> 2];
  176. CPool m_Pools[NUM_POOLS];
  177. SharedData_t *m_pSharedData;
  178. };
  179. //-----------------------------------------------------------------------------
  180. //
  181. //-----------------------------------------------------------------------------
  182. class CStdMemAlloc : public IMemAlloc
  183. {
  184. public:
  185. CStdMemAlloc();
  186. // Internal versions
  187. void *InternalAlloc( int region, size_t nSize );
  188. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  189. void *InternalAllocAligned( int region, size_t nSize, size_t align );
  190. #endif
  191. void *InternalAllocFromPools( size_t nSize );
  192. void *InternalRealloc( void *pMem, size_t nSize );
  193. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  194. void *InternalReallocAligned( void *pMem, size_t nSize, size_t align );
  195. #endif
  196. void InternalFree( void *pMem );
  197. void CompactOnFail();
  198. // Release versions
  199. virtual void *Alloc( size_t nSize );
  200. virtual void *Realloc( void *pMem, size_t nSize );
  201. virtual void Free( void *pMem );
  202. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
  203. // Debug versions
  204. virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
  205. virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
  206. virtual void Free( void *pMem, const char *pFileName, int nLine );
  207. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
  208. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  209. virtual void *AllocAlign( size_t nSize, size_t align );
  210. virtual void *AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine );
  211. virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align );
  212. virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine );
  213. #endif
  214. virtual void *RegionAlloc( int region, size_t nSize );
  215. virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine );
  216. // Returns size of a particular allocation
  217. virtual size_t GetSize( void *pMem );
  218. // Force file + line information for an allocation
  219. virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
  220. virtual void PopAllocDbgInfo();
  221. virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc );
  222. virtual int CrtSetReportMode( int nReportType, int nReportMode );
  223. virtual int CrtIsValidHeapPointer( const void *pMem );
  224. virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
  225. virtual int CrtCheckMemory( void );
  226. virtual int CrtSetDbgFlag( int nNewFlag );
  227. virtual void CrtMemCheckpoint( _CrtMemState *pState );
  228. void* CrtSetReportFile( int nRptType, void* hFile );
  229. void* CrtSetReportHook( void* pfnNewHook );
  230. int CrtDbgReport( int nRptType, const char * szFile,
  231. int nLine, const char * szModule, const char * pMsg );
  232. virtual int heapchk();
  233. virtual void DumpStats();
  234. virtual void DumpStatsFileBase( char const *pchFileBase );
  235. virtual size_t ComputeMemoryUsedBy( char const *pchSubStr );
  236. virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory );
  237. virtual bool IsDebugHeap() { return false; }
  238. virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) {}
  239. virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) {}
  240. virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) {}
  241. virtual int GetVersion() { return MEMALLOC_VERSION; }
  242. virtual void OutOfMemory( size_t nBytesAttempted = 0 ) { SetCRTAllocFailed( nBytesAttempted ); }
  243. virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes );
  244. virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats );
  245. virtual void CompactHeap();
  246. virtual void CompactIncremental();
  247. virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler );
  248. size_t CallAllocFailHandler( size_t nBytes ) { return (*m_pfnFailHandler)( nBytes); }
  249. virtual uint32 GetDebugInfoSize() { return 0; }
  250. virtual void SaveDebugInfo( void *pvDebugInfo ) { }
  251. virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
  252. virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
  253. static size_t DefaultFailHandler( size_t );
  254. void DumpBlockStats( void *p ) {}
  255. #if MEM_SBH_ENABLED
  256. class CVirtualAllocator
  257. {
  258. public:
  259. enum
  260. {
  261. BYTES_PAGE = (64*1024),
  262. TOTAL_BYTES = (32*1024*1024),
  263. MIN_RESERVE_PAGES = 4,
  264. };
  265. byte *AllocatePoolMemory()
  266. {
  267. #ifdef _WIN32
  268. return (byte *)VirtualAlloc( NULL, TOTAL_BYTES, VA_RESERVE_FLAGS, PAGE_NOACCESS );
  269. #elif defined( _PS3 )
  270. Error( "" );
  271. return NULL;
  272. #else
  273. #error
  274. #endif
  275. }
  276. bool IsVirtual()
  277. {
  278. return true;
  279. }
  280. bool Decommit( void *pPage )
  281. {
  282. #ifdef _WIN32
  283. return ( VirtualFree( pPage, BYTES_PAGE, MEM_DECOMMIT ) != 0 );
  284. #elif defined( _PS3 )
  285. return false;
  286. #else
  287. #error
  288. #endif
  289. }
  290. bool Commit( void *pPage )
  291. {
  292. #ifdef _WIN32
  293. return ( VirtualAlloc( pPage, BYTES_PAGE, VA_COMMIT_FLAGS, PAGE_READWRITE ) != NULL );
  294. #elif defined( _PS3 )
  295. return false;
  296. #else
  297. #error
  298. #endif
  299. }
  300. };
  301. typedef CSmallBlockHeap<CVirtualAllocator> CVirtualSmallBlockHeap;
  302. template <size_t SIZE_MB, bool bPhysical>
  303. class CFixedAllocator
  304. {
  305. public:
  306. enum
  307. {
  308. BYTES_PAGE = (16*1024),
  309. TOTAL_BYTES = (SIZE_MB*1024*1024),
  310. MIN_RESERVE_PAGES = TOTAL_BYTES/BYTES_PAGE,
  311. };
  312. byte *AllocatePoolMemory()
  313. {
  314. #ifdef _WIN32
  315. #ifdef _X360
  316. if ( bPhysical )
  317. return (byte *)XPhysicalAlloc( TOTAL_BYTES, MAXULONG_PTR, 4096, PAGE_READWRITE | MEM_16MB_PAGES );
  318. #endif
  319. return (byte *)VirtualAlloc( NULL, TOTAL_BYTES, VA_COMMIT_FLAGS, PAGE_READWRITE );
  320. #elif defined( _PS3 )
  321. // TODO: release this section on shutdown (use GetMemorySectionForAddress)
  322. extern IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes );
  323. IVirtualMemorySection *pSection = VirtualMemoryManager_AllocateVirtualMemorySection( TOTAL_BYTES );
  324. if ( !pSection )
  325. Error( "CFixedAllocator::AllocatePoolMemory() failed in VirtualMemoryManager_AllocateVirtualMemorySection\n" );
  326. if ( !pSection->CommitPages( pSection->GetBaseAddress(), TOTAL_BYTES ) )
  327. Error( "CFixedAllocator::AllocatePoolMemory() failed in IVirtualMemorySection::CommitPages\n" );
  328. return reinterpret_cast<byte *>( pSection->GetBaseAddress() );
  329. #else
  330. #error
  331. #endif
  332. }
  333. bool IsVirtual()
  334. {
  335. return false;
  336. }
  337. bool Decommit( void *pPage )
  338. {
  339. return false;
  340. }
  341. bool Commit( void *pPage )
  342. {
  343. return false;
  344. }
  345. };
  346. typedef CSmallBlockHeap<CFixedAllocator< MBYTES_PRIMARY_SBH, true> > CFixedSmallBlockHeap;
  347. #ifdef MEMALLOC_USE_SECONDARY_SBH
  348. typedef CSmallBlockHeap<CFixedAllocator< MBYTES_SECONDARY_SBH, false> > CFixedVirtualSmallBlockHeap; // @TODO: move back into above heap if number stays at 16 [7/15/2009 tom]
  349. #endif
  350. CFixedSmallBlockHeap m_PrimarySBH;
  351. #ifdef MEMALLOC_USE_SECONDARY_SBH
  352. CFixedVirtualSmallBlockHeap m_SecondarySBH;
  353. #endif
  354. #ifndef MEMALLOC_NO_FALLBACK
  355. CVirtualSmallBlockHeap m_FallbackSBH;
  356. #endif
  357. #endif // MEM_SBH_ENABLED
  358. virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment );
  359. virtual size_t MemoryAllocFailed();
  360. void SetCRTAllocFailed( size_t nMemSize );
  361. MemAllocFailHandler_t m_pfnFailHandler;
  362. size_t m_sMemoryAllocFailed;
  363. CThreadFastMutex m_CompactMutex;
  364. bool m_bInCompact;
  365. };
  366. #ifndef _PS3
  367. #pragma pack()
  368. #endif