diff --git a/Source/Core/Common/MemArena.h b/Source/Core/Common/MemArena.h index adb944acf9..33ebb66eb4 100644 --- a/Source/Core/Common/MemArena.h +++ b/Source/Core/Common/MemArena.h @@ -122,4 +122,42 @@ private: #endif }; +// This class represents a single fixed-size memory region where the individual memory pages are +// only actually allocated on first access. The memory will be zero on first access. +class LazyMemoryRegion final +{ +public: + LazyMemoryRegion(); + ~LazyMemoryRegion(); + LazyMemoryRegion(const LazyMemoryRegion&) = delete; + LazyMemoryRegion(LazyMemoryRegion&&) = delete; + LazyMemoryRegion& operator=(const LazyMemoryRegion&) = delete; + LazyMemoryRegion& operator=(LazyMemoryRegion&&) = delete; + + /// + /// Reserve a memory region. + /// + /// @param size The size of the region. + /// + /// @return The address the region was mapped at. Returns nullptr on failure. + /// + void* Create(size_t size); + + /// + /// Reset the memory region back to zero, throwing away any mapped pages. + /// This can only be called after a successful call to Create(). + /// + void Clear(); + + /// + /// Release the memory previously reserved with Create(). After this call the pointer that was + /// returned by Create() will become invalid. + /// + void Release(); + +private: + void* m_memory = nullptr; + size_t m_size = 0; +}; + } // namespace Common diff --git a/Source/Core/Common/MemArenaAndroid.cpp b/Source/Core/Common/MemArenaAndroid.cpp index 01b210100a..4a9e2f68b4 100644 --- a/Source/Core/Common/MemArenaAndroid.cpp +++ b/Source/Core/Common/MemArenaAndroid.cpp @@ -19,6 +19,7 @@ #include #include +#include "Common/Assert.h" #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" @@ -142,4 +143,46 @@ void MemArena::UnmapFromMemoryRegion(void* view, size_t size) if (retval == MAP_FAILED) NOTICE_LOG_FMT(MEMMAP, "mmap failed"); } + +LazyMemoryRegion::LazyMemoryRegion() = default; + +LazyMemoryRegion::~LazyMemoryRegion() +{ + Release(); +} + +void* LazyMemoryRegion::Create(size_t size) +{ + ASSERT(!m_memory); + + void* memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (!memory) + { + NOTICE_LOG_FMT(MEMMAP, "Memory allocation of {} bytes failed.", size); + return nullptr; + } + + m_memory = memory; + m_size = size; + + return memory; +} + +void LazyMemoryRegion::Clear() +{ + ASSERT(m_memory); + + mmap(m_memory, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); +} + +void LazyMemoryRegion::Release() +{ + if (m_memory) + { + munmap(m_memory, m_size); + m_memory = nullptr; + m_size = 0; + } +} + } // namespace Common diff --git a/Source/Core/Common/MemArenaUnix.cpp b/Source/Core/Common/MemArenaUnix.cpp index 1532699276..452c2c50c8 100644 --- a/Source/Core/Common/MemArenaUnix.cpp +++ b/Source/Core/Common/MemArenaUnix.cpp @@ -16,6 +16,7 @@ #include #include +#include "Common/Assert.h" #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" @@ -108,4 +109,46 @@ void MemArena::UnmapFromMemoryRegion(void* view, size_t size) if (retval == MAP_FAILED) NOTICE_LOG_FMT(MEMMAP, "mmap failed"); } + +LazyMemoryRegion::LazyMemoryRegion() = default; + +LazyMemoryRegion::~LazyMemoryRegion() +{ + Release(); +} + +void* LazyMemoryRegion::Create(size_t size) +{ + ASSERT(!m_memory); + + void* memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (!memory) + { + NOTICE_LOG_FMT(MEMMAP, "Memory allocation of {} bytes failed.", size); + return nullptr; + } + + m_memory = memory; + m_size = size; + + return memory; +} + +void LazyMemoryRegion::Clear() +{ + ASSERT(m_memory); + + mmap(m_memory, m_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); +} + +void LazyMemoryRegion::Release() +{ + if (m_memory) + { + munmap(m_memory, m_size); + m_memory = nullptr; + m_size = 0; + } +} + } // namespace Common diff --git a/Source/Core/Common/MemArenaWin.cpp b/Source/Core/Common/MemArenaWin.cpp index 9d4a88d54a..ebf078f45b 100644 --- a/Source/Core/Common/MemArenaWin.cpp +++ b/Source/Core/Common/MemArenaWin.cpp @@ -433,4 +433,47 @@ void MemArena::UnmapFromMemoryRegion(void* view, size_t size) UnmapViewOfFile(view); } + +LazyMemoryRegion::LazyMemoryRegion() = default; + +LazyMemoryRegion::~LazyMemoryRegion() +{ + Release(); +} + +void* LazyMemoryRegion::Create(size_t size) +{ + ASSERT(!m_memory); + + void* memory = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (!memory) + { + NOTICE_LOG_FMT(MEMMAP, "Memory allocation of {} bytes failed.", size); + return nullptr; + } + + m_memory = memory; + m_size = size; + + return memory; +} + +void LazyMemoryRegion::Clear() +{ + ASSERT(m_memory); + + VirtualFree(m_memory, m_size, MEM_DECOMMIT); + VirtualAlloc(m_memory, m_size, MEM_COMMIT, PAGE_READWRITE); +} + +void LazyMemoryRegion::Release() +{ + if (m_memory) + { + VirtualFree(m_memory, 0, MEM_RELEASE); + m_memory = nullptr; + m_size = 0; + } +} + } // namespace Common diff --git a/Source/Core/Core/PowerPC/JitCommon/JitCache.cpp b/Source/Core/Core/PowerPC/JitCommon/JitCache.cpp index a41ebb71b2..98ce48f24a 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitCache.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitCache.cpp @@ -42,7 +42,11 @@ void JitBaseBlockCache::Init() { Common::JitRegister::Init(Config::Get(Config::MAIN_PERF_MAP_DIR)); - m_block_map_arena.GrabSHMSegment(FAST_BLOCK_MAP_SIZE, "dolphin-emu-jitblock"); + m_fast_block_map = reinterpret_cast(m_block_map_arena.Create(FAST_BLOCK_MAP_SIZE)); + if (m_fast_block_map) + m_fast_block_map_ptr = m_fast_block_map; + else + m_fast_block_map_ptr = m_fast_block_map_fallback.data(); Clear(); } @@ -51,12 +55,7 @@ void JitBaseBlockCache::Shutdown() { Common::JitRegister::Shutdown(); - if (m_fast_block_map) - { - m_block_map_arena.ReleaseView(m_fast_block_map, FAST_BLOCK_MAP_SIZE); - } - - m_block_map_arena.ReleaseSHMSegment(); + m_block_map_arena.Release(); } // This clears the JIT cache. It's called from JitCache.cpp when the JIT cache @@ -80,23 +79,7 @@ void JitBaseBlockCache::Clear() valid_block.ClearAll(); if (m_fast_block_map) - { - m_block_map_arena.ReleaseView(m_fast_block_map, FAST_BLOCK_MAP_SIZE); - m_block_map_arena.ReleaseSHMSegment(); - m_block_map_arena.GrabSHMSegment(FAST_BLOCK_MAP_SIZE, "dolphin-emu-jitblock"); - } - - m_fast_block_map = - reinterpret_cast(m_block_map_arena.CreateView(0, FAST_BLOCK_MAP_SIZE)); - - if (m_fast_block_map) - { - m_fast_block_map_ptr = m_fast_block_map; - } - else - { - m_fast_block_map_ptr = m_fast_block_map_fallback.data(); - } + m_block_map_arena.Clear(); } void JitBaseBlockCache::Reset() diff --git a/Source/Core/Core/PowerPC/JitCommon/JitCache.h b/Source/Core/Core/PowerPC/JitCommon/JitCache.h index d3f353e815..cf6f785d98 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitCache.h +++ b/Source/Core/Core/PowerPC/JitCommon/JitCache.h @@ -212,7 +212,7 @@ private: // This is used as a fast cache of block_map used in the assembly dispatcher. // It is implemented via a shm segment using m_block_map_arena. JitBlock** m_fast_block_map = 0; - Common::MemArena m_block_map_arena; + Common::LazyMemoryRegion m_block_map_arena; // An alternative for the above fast_block_map but without a shm segment // in case the shm memory region couldn't be allocated.