//===== Copyright © 2010, Valve Corporation, All rights reserved. ======// // // Purpose: Virtual memory sections management! // // $NoKeywords: $ //===========================================================================// #include "pch_tier0.h" #if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) //#include #include #include "tier0/dbg.h" #include "tier0/stacktools.h" #include "tier0/memalloc.h" #include "tier0/memvirt.h" #include "tier0/fasttimer.h" #include "mem_helpers.h" #ifdef PLATFORM_WINDOWS_PC #undef WIN32_LEAN_AND_MEAN #include #include #endif #ifdef OSX #include #include #endif #include #include #include #include "tier0/threadtools.h" #ifdef _X360 #include "xbox/xbox_console.h" #endif #ifdef _PS3 #include "tls_ps3.h" #include "memoverride_ps3.h" #include "sys/memory.h" #include "sys/mempool.h" #include "sys/process.h" #include "sys/vm.h" #endif CInterlockedInt VmmMsgFlag = 0; // Prevents re-entrancy within VmmMsg (printf allocates a large buffer!) #ifdef _DEBUG #ifdef _PS3 // _DEBUG #define VmmMsg( mutex, ... ) ( (VmmMsgFlag|mutex.GetOwnerId()) ? 0 : ( ++VmmMsgFlag, Msg( __VA_ARGS__ ), VmmMsgFlag-- ) ) #else #define VmmMsg( mutex, ... ) ( (VmmMsgFlag|mutex.GetOwnerId()) ? 0 : ( ++VmmMsgFlag, DevMsg( __VA_ARGS__ ), VmmMsgFlag-- ) ) #endif #else #define VmmMsg( mutex, ... ) ((void)0) #endif #if 0 #define TRACE_CALL( ... ) do { FILE *fPs3Trace = fopen( "/app_home/tracevmm.txt", "a+" ); if( fPs3Trace ) { fprintf( fPs3Trace, __VA_ARGS__ ); fclose( fPs3Trace ); } } while( 0 ) #else #define TRACE_CALL( ... ) ((void)0) #endif #ifdef _PS3 #define VIRTUAL_MEMORY_MANAGER_SUPPORTED #define VMM_SYSTEM_PAGE_POLICY SYS_MEMORY_PAGE_SIZE_64K #define VMM_SYSTEM_PAGE_ALLOCFLAGS SYS_MEMORY_GRANULARITY_64K #define VMM_POLICY_SYS_VM 0 // sys_vm_* system is really buggy and console sometimes hardlocks or crashes in OS #define VMM_POLICY_SYS_MMAPPER 1 // sys_mmapper_* seems fairly stable #define VMM_POLICY_SYS_VM_NO_RETURN 1 // looks like returning memory under sys_vm_* is the main reason for hardlocks #if !(VMM_POLICY_SYS_VM) == !(VMM_POLICY_SYS_MMAPPER) #error #endif class CVirtualMemoryManager { public: CVirtualMemoryManager(); ~CVirtualMemoryManager() { Shutdown(); } void Shutdown(); IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes ); IVirtualMemorySection * NewSection( byte *pBase, size_t numMaxBytes ); IVirtualMemorySection * GetMemorySectionForAddress( void *pAddress ); void GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax ); public: byte *m_pBase; // base address of the virtual address space size_t m_nPhysicalSize; // physical memory committed size_t m_nPhysicalSizeMax; // physical memory committed (max since startup) size_t m_nVirtualSize; // virtual memory reserved size_t m_nVirtualSizeMax; // virtual memory reserved (max since startup) #if VMM_POLICY_SYS_VM size_t m_nSparePhysicalSize; // physical memory that has been decommitted, but hasn't been returned #endif #if VMM_POLICY_SYS_MMAPPER sys_memory_t m_sysMemPages[ VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE ]; #endif IVirtualMemorySection *m_uiVirtualMap[ VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE ]; // map of pages to sections uint32 m_uiCommitMask[ VMM_VIRTUAL_SIZE / ( VMM_PAGE_SIZE * 32 ) ]; // mask of committed pages inline bool CommitTest( int iPage ) const { const uint32 mask = 1u << ( iPage % 32 ), &word = m_uiCommitMask[ iPage / 32 ]; return !!( word & mask ); } inline void CommitSet( int iPage, bool bSet ) { uint32 mask = 1u << ( iPage % 32 ), &word = m_uiCommitMask[ iPage / 32 ]; word = ( word & ~mask ) | ( bSet ? mask : 0 ); } inline size_t CountReservedPages() { size_t uResult = 0; for ( int k = 0; k < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ k ) uResult += m_uiVirtualMap[k] ? 1 : 0; return uResult; } inline size_t CountCommittedPages() const { size_t uResult = 0; for ( int k = 0; k < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ k ) uResult += CommitTest(k) ? 1 : 0; return uResult; } public: bool CommitPage( byte *pbAddress ); bool DecommitPage( byte *pbAddress ); public: CThreadFastMutex m_Mutex; }; static CVirtualMemoryManager& GetVirtualMemoryManager() { static CVirtualMemoryManager s_VirtualMemoryManager; return s_VirtualMemoryManager; } CVirtualMemoryManager::CVirtualMemoryManager() : m_pBase( NULL ), #if VMM_POLICY_SYS_VM m_nSparePhysicalSize( 0 ), #endif m_nPhysicalSize( 0 ), m_nPhysicalSizeMax( 0 ), m_nVirtualSize( 0 ), m_nVirtualSizeMax( 0 ) { memset( m_uiVirtualMap, 0, sizeof( m_uiVirtualMap ) ); memset( m_uiCommitMask, 0, sizeof( m_uiCommitMask ) ); #if VMM_POLICY_SYS_VM TRACE_CALL( "sys_vm_memory_map( 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X ) ... ", VMM_VIRTUAL_SIZE, VMM_PAGE_SIZE, SYS_MEMORY_CONTAINER_ID_INVALID, VMM_SYSTEM_PAGE_POLICY, SYS_VM_POLICY_AUTO_RECOMMENDED, reinterpret_cast( &m_pBase ) ); int retval = sys_vm_memory_map( VMM_VIRTUAL_SIZE, VMM_PAGE_SIZE, SYS_MEMORY_CONTAINER_ID_INVALID, VMM_SYSTEM_PAGE_POLICY, SYS_VM_POLICY_AUTO_RECOMMENDED, reinterpret_cast( &m_pBase ) ); TRACE_CALL( "ret = 0x%08X, base = 0x%08X\n", retval, m_pBase ); if ( retval < CELL_OK || !m_pBase ) { Error( "sys_vm_memory_map failed( size=%dKB, page=%dKB ), error=0x%08X\n", VMM_VIRTUAL_SIZE / VMM_KB, VMM_PAGE_SIZE / VMM_KB, retval ); Assert( 0 ); m_pBase = NULL; return; } #endif #if VMM_POLICY_SYS_MMAPPER memset( m_sysMemPages, 0, sizeof( m_sysMemPages ) ); TRACE_CALL( "sys_mmapper_allocate_address( 0x%08X, 0x%08X, 0x%08X, 0x%08X ) ... ", VMM_VIRTUAL_SIZE, VMM_SYSTEM_PAGE_POLICY, VMM_VIRTUAL_SIZE, reinterpret_cast( &m_pBase ) ); int retval = sys_mmapper_allocate_address( VMM_VIRTUAL_SIZE, VMM_SYSTEM_PAGE_POLICY, VMM_VIRTUAL_SIZE, reinterpret_cast( &m_pBase ) ); TRACE_CALL( "ret = 0x%08X, base = 0x%08X\n", retval, m_pBase ); if ( retval < CELL_OK || !m_pBase ) { Error( "sys_mmapper_allocate_address failed( size=%dKB, page=%dKB ), error=0x%08X\n", VMM_VIRTUAL_SIZE / VMM_KB, VMM_PAGE_SIZE / VMM_KB, retval ); Assert( 0 ); m_pBase = NULL; return; } #endif VmmMsg( m_Mutex, "Virtual Memory Manager: reserved %uKB block at address 0x%08X.\n", VMM_VIRTUAL_SIZE / VMM_KB, m_pBase ); } void CVirtualMemoryManager::Shutdown() { if ( !m_pBase ) return; if ( m_nPhysicalSize ) { VmmMsg( m_Mutex, "Virtual Memory Manager: shutting down with %uKB allocated!\n", m_nPhysicalSize / VMM_KB ); } #if VMM_POLICY_SYS_VM TRACE_CALL( "sys_vm_unmap( 0x%08X ) ... ", (sys_addr_t) m_pBase ); int retval = sys_vm_unmap( (sys_addr_t) m_pBase ); TRACE_CALL( "ret = 0x%08X\n", retval ); #endif #if VMM_POLICY_SYS_MMAPPER TRACE_CALL( "sys_mmapper_free_address( 0x%08X ) ... ", (sys_addr_t) m_pBase ); int retval = sys_mmapper_free_address( (sys_addr_t) m_pBase ); TRACE_CALL( "ret = 0x%08X\n", retval ); #endif VmmMsg( m_Mutex, "Virtual Memory Manager: unmapped %uKB block at address 0x%08X (result=0x%08X).\n", VMM_VIRTUAL_SIZE / VMM_KB, m_pBase, retval ); m_pBase = NULL; } IVirtualMemorySection * CVirtualMemoryManager::GetMemorySectionForAddress( void *pAddress ) { Assert( ( pAddress >= m_pBase ) && ( pAddress < ( m_pBase + VMM_VIRTUAL_SIZE ) ) ); int iPage = ( (byte*)pAddress - m_pBase ) / VMM_PAGE_SIZE; return m_uiVirtualMap[ iPage ]; } IVirtualMemorySection * CVirtualMemoryManager::AllocateVirtualMemorySection( size_t numMaxBytes ) { Assert( m_pBase ); if ( !m_pBase ) return NULL; // Find the smallest free block with size >= numMaxBytes IVirtualMemorySection *pResult = NULL; int iGoodPage = -1; int iGoodSize = 0; size_t numPages = numMaxBytes / VMM_PAGE_SIZE; if ( !numPages ) return NULL; { AUTO_LOCK( m_Mutex ); // TODO: fill the address range with reserved/free sections (so we iterate 50 sections rather than 8000 pages!) for ( int iTryPage = 0; iTryPage < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ iTryPage ) { if ( m_uiVirtualMap[ iTryPage ] ) continue; // page is taken int iTryPageStart = iTryPage; int iTryPageStride = 1; for ( ++ iTryPage; iTryPage < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ iTryPage, ++ iTryPageStride ) if ( m_uiVirtualMap[ iTryPage ] ) break; if ( iTryPageStride < numPages ) continue; if ( ( iGoodPage < 0 ) || ( iTryPageStride < iGoodSize ) ) { iGoodPage = iTryPageStart; iGoodSize = iTryPageStride; } } if ( iGoodPage >= 0 ) { byte *pbAddress = m_pBase + iGoodPage * VMM_PAGE_SIZE; pResult = NewSection( pbAddress, numMaxBytes ); m_nVirtualSize += numPages*VMM_PAGE_SIZE; m_nVirtualSizeMax = MAX( m_nVirtualSize, m_nVirtualSizeMax ); // Mark pages used for ( int k = 0; k < numPages; ++ k ) m_uiVirtualMap[ iGoodPage + k ] = pResult; } } if ( pResult ) { // NOTE: don't spew while the mutex is held! VmmMsg( m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: new reservation %uKB @ 0x%08X\n", CountReservedPages()*VMM_PAGE_SIZE / VMM_KB, CountCommittedPages()*VMM_PAGE_SIZE / VMM_KB, numMaxBytes / VMM_KB, pResult->GetBaseAddress() ); return pResult; } Error( "CVirtualMemoryManager::AllocateVirtualMemorySection has no memory for %u bytes!\n", numMaxBytes ); Assert( 0 ); return NULL; } bool CVirtualMemoryManager::CommitPage( byte *pbAddress ) { Assert( m_pBase ); int iPage = ( pbAddress - m_pBase ) / VMM_PAGE_SIZE; if ( CommitTest( iPage ) ) return true; CommitSet( iPage, true ); #if VMM_POLICY_SYS_VM if ( m_nPhysicalSize ) { if ( m_nSparePhysicalSize > 0 ) { m_nSparePhysicalSize -= VMM_PAGE_SIZE; } else { TRACE_CALL( "sys_vm_append_memory( 0x%08X, 0x%08X ) ... ", (sys_addr_t) m_pBase, VMM_PAGE_SIZE ); int retval = sys_vm_append_memory( (sys_addr_t) m_pBase, VMM_PAGE_SIZE ); TRACE_CALL( "ret = 0x%08X\n", retval ); if ( retval < CELL_OK ) { VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_vm_append_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB ); return false; } } } m_nPhysicalSize += VMM_PAGE_SIZE; m_nPhysicalSizeMax = MAX( m_nPhysicalSize, m_nPhysicalSizeMax ); TRACE_CALL( "sys_vm_touch( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pbAddress, VMM_PAGE_SIZE ); int retval = sys_vm_touch( (sys_addr_t) pbAddress, VMM_PAGE_SIZE ); TRACE_CALL( "ret = 0x%08X\n", retval ); if ( retval < CELL_OK ) { VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_vm_touch failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB ); return false; } #endif #if VMM_POLICY_SYS_MMAPPER TRACE_CALL( "sys_mmapper_allocate_memory( 0x%08X, 0x%08X, page=%d ) ... ", VMM_PAGE_SIZE, VMM_SYSTEM_PAGE_ALLOCFLAGS, iPage ); int retval = sys_mmapper_allocate_memory( VMM_PAGE_SIZE, VMM_SYSTEM_PAGE_ALLOCFLAGS, &m_sysMemPages[iPage] ); TRACE_CALL( "ret = 0x%08X, mem = 0x%08X\n", retval, m_sysMemPages[iPage] ); if ( retval < CELL_OK ) { VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_mmapper_allocate_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB ); return false; } m_nPhysicalSize += VMM_PAGE_SIZE; m_nPhysicalSizeMax = MAX( m_nPhysicalSize, m_nPhysicalSizeMax ); TRACE_CALL( "sys_mmapper_map_memory( 0x%08X, 0x%08X, 0x%08X, page=%d ) ... ", (sys_addr_t) pbAddress, m_sysMemPages[iPage], SYS_MEMORY_PROT_READ_WRITE, iPage ); retval = sys_mmapper_map_memory( (sys_addr_t) pbAddress, m_sysMemPages[iPage], SYS_MEMORY_PROT_READ_WRITE ); TRACE_CALL( "ret = 0x%08X\n", retval ); if ( retval < CELL_OK ) { VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_mmapper_map_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB ); return false; } #endif return true; } bool CVirtualMemoryManager::DecommitPage( byte *pbAddress ) { Assert( m_pBase ); int iPage = ( pbAddress - m_pBase ) / VMM_PAGE_SIZE; if ( !CommitTest( iPage ) ) return false; CommitSet( iPage, false ); #if VMM_POLICY_SYS_VM TRACE_CALL( "sys_vm_invalidate( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pbAddress, VMM_PAGE_SIZE ); int retval = sys_vm_invalidate( (sys_addr_t) pbAddress, VMM_PAGE_SIZE ); TRACE_CALL( "ret = 0x%08X\n", retval ); if ( retval < CELL_OK ) { VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_vm_invalidate failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB ); return false; } m_nPhysicalSize -= VMM_PAGE_SIZE; #if VMM_POLICY_SYS_VM_NO_RETURN m_nSparePhysicalSize += VMM_PAGE_SIZE; return false; #else return m_nPhysicalSize >= VMM_PAGE_SIZE; #endif #endif #if VMM_POLICY_SYS_MMAPPER TRACE_CALL( "sys_mmapper_unmap_memory( 0x%08X, 0x%08X, page=%d ) ... ", (sys_addr_t) pbAddress, m_sysMemPages[iPage], iPage ); int retval = sys_mmapper_unmap_memory( (sys_addr_t) pbAddress, &m_sysMemPages[iPage] ); TRACE_CALL( "ret = 0x%08X, mem = 0x%08X\n", retval, m_sysMemPages[iPage] ); if ( retval < CELL_OK ) { VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_mmapper_unmap_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB ); return false; } TRACE_CALL( "sys_mmapper_free_memory( 0x%08X, page=%d ) ... ", m_sysMemPages[iPage], iPage ); retval = sys_mmapper_free_memory( m_sysMemPages[iPage] ); TRACE_CALL( "ret = 0x%08X\n", retval ); m_sysMemPages[iPage] = 0; if ( retval < CELL_OK ) { VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_mmapper_free_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB ); return false; } m_nPhysicalSize -= VMM_PAGE_SIZE; return true; #endif } void CVirtualMemoryManager::GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax ) { AUTO_LOCK( m_Mutex ); nReserved = m_nVirtualSize; nReservedMax = m_nVirtualSizeMax; nCommitted = m_nPhysicalSize; nCommittedMax = m_nPhysicalSizeMax; } class CVirtualMemorySectionImpl : public IVirtualMemorySection { public: CVirtualMemorySectionImpl( byte *pbAddress, size_t numMaxBytes ) : m_pBase( pbAddress ), m_numMaxBytes( numMaxBytes ), m_numPhysical( 0 ) { } // Information about memory section virtual void * GetBaseAddress() { return m_pBase; } virtual size_t GetPageSize() { return VMM_PAGE_SIZE; } virtual size_t GetTotalSize() { return m_numMaxBytes; } // Functions to manage physical memory mapped to virtual memory virtual bool CommitPages( void *pvBase, size_t numBytes ); virtual void DecommitPages( void *pvBase, size_t numBytes ); bool CommitPages_Inner( void *pvBase, size_t numBytes ); // Release the physical memory and associated virtual address space virtual void Release(); byte *m_pBase; size_t m_numMaxBytes; size_t m_numPhysical; }; bool CVirtualMemorySectionImpl::CommitPages_Inner( void *pvBase, size_t numBytes ) { Assert( pvBase >= m_pBase ); Assert( ( (byte*)pvBase ) + numBytes <= m_pBase + m_numMaxBytes ); { AUTO_LOCK( GetVirtualMemoryManager().m_Mutex ); int startPage = ( ( (byte*)pvBase ) - m_pBase ) / VMM_PAGE_SIZE; int endPage = ( ( (byte*)pvBase ) + numBytes + ( VMM_PAGE_SIZE - 1 ) - m_pBase ) / VMM_PAGE_SIZE; for ( int k = startPage; k < endPage; k++ ) { if ( !GetVirtualMemoryManager().CommitPage( m_pBase + k * VMM_PAGE_SIZE ) ) { // Failure! Decommit the pages we have committed so far: for ( k = k - 1; k >= startPage; k-- ) { GetVirtualMemoryManager().DecommitPage( m_pBase + k * VMM_PAGE_SIZE ); } return false; } } #if VMM_POLICY_SYS_VM TRACE_CALL( "sys_vm_sync( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pvBase, numBytes ); int retval = sys_vm_sync( (sys_addr_t) pvBase, numBytes ); TRACE_CALL( "ret = 0x%08X\n", retval ); if ( retval < CELL_OK ) { VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: CommitPages: sys_vm_sync failed (result=0x%08X).\n", retval ); return false; } #endif } // NOTE: we don't spew while the mutex is held! VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: committed %uKB @ 0x%08X\n", GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB, GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB, numBytes / VMM_KB, pvBase ); return true; } bool CVirtualMemorySectionImpl::CommitPages( void *pvBase, size_t numBytes ) { if ( CommitPages_Inner( pvBase, numBytes ) ) return true; // On failure, compact the heap and try one last time: Msg( "\n\nVirtual Memory Manager: COMMIT FAILED! (%d) Last-ditch effort: compacting the heap and re-trying...\n", numBytes ); g_pMemAllocInternalPS3->CompactHeap(); bool success = CommitPages_Inner( pvBase, numBytes ); if ( !success ) { Msg( "Virtual Memory Manager: COMMIT FAILED! (%d) Fatal error.\n\n\n", numBytes ); g_pMemAllocInternalPS3->OutOfMemory( numBytes ); } Msg("\n\n"); return success; } void CVirtualMemorySectionImpl::DecommitPages( void *pvBase, size_t numBytes ) { Assert( pvBase >= m_pBase ); Assert( ( (byte*)pvBase ) + numBytes <= m_pBase + m_numMaxBytes ); { AUTO_LOCK( GetVirtualMemoryManager().m_Mutex ); int numPagesDecommitted = 0; for ( int k = ( ( (byte*)pvBase ) - m_pBase ) / VMM_PAGE_SIZE; m_pBase + k * VMM_PAGE_SIZE < ( (byte*)pvBase ) + numBytes; ++ k ) { numPagesDecommitted += !!GetVirtualMemoryManager().DecommitPage( m_pBase + k * VMM_PAGE_SIZE ); } #if VMM_POLICY_SYS_VM TRACE_CALL( "sys_vm_sync( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pvBase, numBytes ); int retval = sys_vm_sync( (sys_addr_t) pvBase, numBytes ); TRACE_CALL( "ret = 0x%08X\n", retval ); if ( retval < CELL_OK ) { VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: DecommitPages: sys_vm_sync failed (result=0x%08X).\n", retval ); } if ( numPagesDecommitted > 0 ) { TRACE_CALL( "sys_vm_return_memory( 0x%08X, 0x%08X ) ... ", (sys_addr_t) GetVirtualMemoryManager().m_pBase, numPagesDecommitted * VMM_PAGE_SIZE ); retval = sys_vm_return_memory( (sys_addr_t) GetVirtualMemoryManager().m_pBase, numPagesDecommitted * VMM_PAGE_SIZE ); TRACE_CALL( "ret = 0x%08X\n", retval ); if ( retval < CELL_OK ) { VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: DecommitPages: sys_vm_return_memory failed (result=0x%08X).\n", retval ); } } #endif } // NOTE: we don't spew while the mutex is held! VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: decommitted %uKB @ 0x%08X\n", GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB, GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB, numBytes / VMM_KB, pvBase ); } void CVirtualMemorySectionImpl::Release() { DecommitPages( m_pBase, m_numMaxBytes ); { AUTO_LOCK( GetVirtualMemoryManager().m_Mutex ); int iPageStart = ( m_pBase - GetVirtualMemoryManager().m_pBase ) / VMM_PAGE_SIZE; for ( int k = 0; m_pBase + k * VMM_PAGE_SIZE < m_pBase + m_numMaxBytes; ++ k ) { Assert( GetVirtualMemoryManager().m_uiVirtualMap[ iPageStart + k ] == this ); GetVirtualMemoryManager().m_uiVirtualMap[ iPageStart + k ] = NULL; GetVirtualMemoryManager().m_nVirtualSize -= VMM_PAGE_SIZE; } } // NOTE: we don't spew while the mutex is held! VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: released %uKB @ 0x%08X\n", GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB, GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB, m_numMaxBytes / VMM_KB, m_pBase ); delete this; } IVirtualMemorySection * CVirtualMemoryManager::NewSection( byte *pBase, size_t numMaxBytes ) { return new CVirtualMemorySectionImpl( pBase, numMaxBytes ); } #endif #ifdef VIRTUAL_MEMORY_MANAGER_SUPPORTED IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes ) { return GetVirtualMemoryManager().AllocateVirtualMemorySection( numMaxBytes ); } void VirtualMemoryManager_Shutdown() { GetVirtualMemoryManager().Shutdown(); } IVirtualMemorySection *GetMemorySectionForAddress( void *pAddress ) { return GetVirtualMemoryManager().GetMemorySectionForAddress( pAddress ); } void VirtualMemoryManager_GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax ) { GetVirtualMemoryManager().GetStats( nReserved, nReservedMax, nCommitted, nCommittedMax ); } #else IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes ) { return NULL; } void VirtualMemoryManager_Shutdown() { } IVirtualMemorySection *GetMemorySectionForAddress( void *pAddress ) { return NULL; } #endif #endif // !STEAM && !NO_MALLOC_OVERRIDE