diff --git a/src/xenia/base/memory.h b/src/xenia/base/memory.h index e1125a5e3..45471e50f 100644 --- a/src/xenia/base/memory.h +++ b/src/xenia/base/memory.h @@ -18,6 +18,7 @@ #include "xenia/base/assert.h" #include "xenia/base/byte_order.h" +#include "xenia/base/platform.h" namespace xe { namespace memory { @@ -96,12 +97,21 @@ void AlignedFree(T* ptr) { #endif // XE_COMPILER_MSVC } +#if XE_PLATFORM_WIN32 +// HANDLE. typedef void* FileMappingHandle; +constexpr FileMappingHandle kFileMappingHandleInvalid = nullptr; +#else +// File descriptor. +typedef int FileMappingHandle; +constexpr FileMappingHandle kFileMappingHandleInvalid = -1; +#endif FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path, size_t length, PageAccess access, bool commit); -void CloseFileMappingHandle(FileMappingHandle handle); +void CloseFileMappingHandle(FileMappingHandle handle, + const std::filesystem::path& path); void* MapFileView(FileMappingHandle handle, void* base_address, size_t length, PageAccess access, size_t file_offset); bool UnmapFileView(FileMappingHandle handle, void* base_address, size_t length); diff --git a/src/xenia/base/memory_posix.cc b/src/xenia/base/memory_posix.cc index 8010e5f18..f2834fb9d 100644 --- a/src/xenia/base/memory_posix.cc +++ b/src/xenia/base/memory_posix.cc @@ -40,7 +40,13 @@ void* AllocFixed(void* base_address, size_t length, AllocationType allocation_type, PageAccess access) { // mmap does not support reserve / commit, so ignore allocation_type. uint32_t prot = ToPosixProtectFlags(access); - return mmap(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + void* result = mmap(base_address, length, prot, + MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); + if (result == MAP_FAILED) { + return nullptr; + } else { + return result; + } } bool DeallocFixed(void* base_address, size_t length, @@ -80,25 +86,28 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path, assert_always(); return nullptr; } - oflag |= O_CREAT; - int ret = shm_open(path.c_str(), oflag, 0777); - if (ret > 0) { - ftruncate64(ret, length); + auto full_path = "/" / path; + int ret = shm_open(full_path.c_str(), oflag, 0777); + if (ret < 0) { + return kFileMappingHandleInvalid; } - - return ret <= 0 ? nullptr : reinterpret_cast(ret); + ftruncate64(ret, length); + return ret; } -void CloseFileMappingHandle(FileMappingHandle handle) { - close((intptr_t)handle); +void CloseFileMappingHandle(FileMappingHandle handle, + const std::filesystem::path& path) { + close(handle); + auto full_path = "/" / path; + shm_unlink(full_path.c_str()); } void* MapFileView(FileMappingHandle handle, void* base_address, size_t length, PageAccess access, size_t file_offset) { uint32_t prot = ToPosixProtectFlags(access); - return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, - reinterpret_cast(handle), file_offset); + return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, handle, + file_offset); } bool UnmapFileView(FileMappingHandle handle, void* base_address, diff --git a/src/xenia/base/memory_win.cc b/src/xenia/base/memory_win.cc index 13b78a411..343285d94 100644 --- a/src/xenia/base/memory_win.cc +++ b/src/xenia/base/memory_win.cc @@ -147,12 +147,16 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path, bool commit) { DWORD protect = ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE); + auto full_path = "Local" / path; return CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, protect, static_cast(length >> 32), - static_cast(length), path.c_str()); + static_cast(length), full_path.c_str()); } -void CloseFileMappingHandle(FileMappingHandle handle) { CloseHandle(handle); } +void CloseFileMappingHandle(FileMappingHandle handle, + const std::filesystem::path& path) { + CloseHandle(handle); +} void* MapFileView(FileMappingHandle handle, void* base_address, size_t length, PageAccess access, size_t file_offset) { diff --git a/src/xenia/base/testing/memory_test.cc b/src/xenia/base/testing/memory_test.cc index 5433dc118..7a19a55a4 100644 --- a/src/xenia/base/testing/memory_test.cc +++ b/src/xenia/base/testing/memory_test.cc @@ -10,6 +10,9 @@ #include "xenia/base/memory.h" #include "third_party/catch/include/catch.hpp" +#include "third_party/fmt/include/fmt/format.h" + +#include "xenia/base/clock.h" namespace xe { namespace base { @@ -414,6 +417,58 @@ TEST_CASE("copy_and_swap_16_in_32_unaligned", "Copy and Swap") { REQUIRE(true == true); } +TEST_CASE("create_and_close_file_mapping", "Virtual Memory Mapping") { + auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount()); + auto memory = xe::memory::CreateFileMappingHandle( + path, 0x100, xe::memory::PageAccess::kReadWrite, true); + REQUIRE(memory != xe::memory::FileMappingHandleInvalid); + xe::memory::CloseFileMappingHandle(memory, path); +} + +TEST_CASE("map_view", "Virtual Memory Mapping") { + auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount()); + const size_t length = 0x100; + auto memory = xe::memory::CreateFileMappingHandle( + path, length, xe::memory::PageAccess::kReadWrite, true); + REQUIRE(memory != xe::memory::FileMappingHandleInvalid); + + uintptr_t address = 0x100000000; + auto view = + xe::memory::MapFileView(memory, reinterpret_cast(address), length, + xe::memory::PageAccess::kReadWrite, 0); + REQUIRE(reinterpret_cast(view) == address); + + xe::memory::UnmapFileView(memory, reinterpret_cast(address), length); + xe::memory::CloseFileMappingHandle(memory, path); +} + +TEST_CASE("read_write_view", "Virtual Memory Mapping") { + const size_t length = 0x100; + auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount()); + auto memory = xe::memory::CreateFileMappingHandle( + path, length, xe::memory::PageAccess::kReadWrite, true); + REQUIRE(memory != xe::memory::FileMappingHandleInvalid); + + uintptr_t address = 0x100000000; + auto view = + xe::memory::MapFileView(memory, reinterpret_cast(address), length, + xe::memory::PageAccess::kReadWrite, 0); + REQUIRE(reinterpret_cast(view) == address); + + for (uint32_t i = 0; i < length; i += sizeof(uint8_t)) { + auto p_value = reinterpret_cast(address + i); + *p_value = i; + } + for (uint32_t i = 0; i < length; i += sizeof(uint8_t)) { + auto p_value = reinterpret_cast(address + i); + uint8_t value = *p_value; + REQUIRE(value == i); + } + + xe::memory::UnmapFileView(memory, reinterpret_cast(address), length); + xe::memory::CloseFileMappingHandle(memory, path); +} + } // namespace test } // namespace base } // namespace xe diff --git a/src/xenia/base/testing/threading_test.cc b/src/xenia/base/testing/threading_test.cc index f8fae6339..ba6eda0db 100644 --- a/src/xenia/base/testing/threading_test.cc +++ b/src/xenia/base/testing/threading_test.cc @@ -813,7 +813,7 @@ TEST_CASE("Create and Run Thread", "Thread") { result = Wait(Thread::GetCurrentThread(), false, 50ms); REQUIRE(result == WaitResult::kTimeout); - params.stack_size = 16 * 1024; + params.stack_size = 16 * 1024 * 1024; thread = Thread::Create(params, [] { while (true) { Thread::Exit(-1); diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index 9e39b17a5..7194855f2 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -505,6 +505,7 @@ class PosixCondition : public PosixConditionBase { } } if (pthread_create(&thread_, &attr, ThreadStartRoutine, start_data) != 0) { + pthread_attr_destroy(&attr); return false; } pthread_attr_destroy(&attr); @@ -1006,7 +1007,10 @@ Thread* Thread::GetCurrentThread() { pthread_t handle = pthread_self(); current_thread_ = new PosixThread(handle); - atexit([] { delete current_thread_; }); + // TODO(bwrsandman): Disabling deleting thread_local current thread to prevent + // assert in destructor. Since this is thread local, the + // "memory leaking" is controlled. + // atexit([] { delete current_thread_; }); return current_thread_; } diff --git a/src/xenia/cpu/backend/x64/x64_code_cache.cc b/src/xenia/cpu/backend/x64/x64_code_cache.cc index 8f9b433dd..d3b22d14c 100644 --- a/src/xenia/cpu/backend/x64/x64_code_cache.cc +++ b/src/xenia/cpu/backend/x64/x64_code_cache.cc @@ -40,11 +40,11 @@ X64CodeCache::~X64CodeCache() { } // Unmap all views and close mapping. - if (mapping_) { + if (mapping_ != xe::memory::kFileMappingHandleInvalid) { xe::memory::UnmapFileView(mapping_, generated_code_base_, kGeneratedCodeSize); - xe::memory::CloseFileMappingHandle(mapping_); - mapping_ = nullptr; + xe::memory::CloseFileMappingHandle(mapping_, file_name_); + mapping_ = xe::memory::kFileMappingHandleInvalid; } } @@ -63,12 +63,11 @@ bool X64CodeCache::Initialize() { } // Create mmap file. This allows us to share the code cache with the debugger. - file_name_ = - fmt::format("Local\\xenia_code_cache_{}", Clock::QueryHostTickCount()); + file_name_ = fmt::format("xenia_code_cache_{}", Clock::QueryHostTickCount()); mapping_ = xe::memory::CreateFileMappingHandle( file_name_, kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite, false); - if (!mapping_) { + if (mapping_ == xe::memory::kFileMappingHandleInvalid) { XELOGE("Unable to create code cache mmap"); return false; } diff --git a/src/xenia/cpu/backend/x64/x64_code_cache.h b/src/xenia/cpu/backend/x64/x64_code_cache.h index 1f1473094..9f7424ec8 100644 --- a/src/xenia/cpu/backend/x64/x64_code_cache.h +++ b/src/xenia/cpu/backend/x64/x64_code_cache.h @@ -100,7 +100,8 @@ class X64CodeCache : public CodeCache { UnwindReservation unwind_reservation) {} std::filesystem::path file_name_; - xe::memory::FileMappingHandle mapping_ = nullptr; + xe::memory::FileMappingHandle mapping_ = + xe::memory::kFileMappingHandleInvalid; // NOTE: the global critical region must be held when manipulating the offsets // or counts of anything, to keep the tables consistent and ordered. diff --git a/src/xenia/kernel/util/shim_utils.h b/src/xenia/kernel/util/shim_utils.h index fcfc90d4d..126358b17 100644 --- a/src/xenia/kernel/util/shim_utils.h +++ b/src/xenia/kernel/util/shim_utils.h @@ -148,7 +148,7 @@ class Param { protected: Param() : ordinal_(-1) {} - explicit Param(Init& init) : ordinal_(--init.ordinal) {} + explicit Param(Init& init) : ordinal_(init.ordinal++) {} template void LoadValue(Init& init, V* out_value) { @@ -519,10 +519,13 @@ xe::cpu::Export* RegisterExport(R (*fn)(Ps&...), const char* name, ++export_entry->function_data.call_count; Param::Init init = { ppc_context, - sizeof...(Ps), 0, }; - auto params = std::make_tuple(Ps(init)...); + // Using braces initializer instead of make_tuple because braces + // enforce execution order across compilers. + // The make_tuple order is undefined per the C++ standard and + // cause inconsitencies between msvc and clang. + std::tuple params = {Ps(init)...}; if (export_entry->tags & xe::cpu::ExportTag::kLog && (!(export_entry->tags & xe::cpu::ExportTag::kHighFrequency) || cvars::log_high_frequency_kernel_calls)) { @@ -554,9 +557,13 @@ xe::cpu::Export* RegisterExport(void (*fn)(Ps&...), const char* name, ++export_entry->function_data.call_count; Param::Init init = { ppc_context, - sizeof...(Ps), + 0, }; - auto params = std::make_tuple(Ps(init)...); + // Using braces initializer instead of make_tuple because braces + // enforce execution order across compilers. + // The make_tuple order is undefined per the C++ standard and + // cause inconsitencies between msvc and clang. + std::tuple params = {Ps(init)...}; if (export_entry->tags & xe::cpu::ExportTag::kLog && (!(export_entry->tags & xe::cpu::ExportTag::kHighFrequency) || cvars::log_high_frequency_kernel_calls)) { diff --git a/src/xenia/memory.cc b/src/xenia/memory.cc index 7e60797f5..f70e00f66 100644 --- a/src/xenia/memory.cc +++ b/src/xenia/memory.cc @@ -113,11 +113,11 @@ Memory::~Memory() { heaps_.physical.Dispose(); // Unmap all views and close mapping. - if (mapping_) { + if (mapping_ != xe::memory::kFileMappingHandleInvalid) { UnmapViews(); - xe::memory::CloseFileMappingHandle(mapping_); + xe::memory::CloseFileMappingHandle(mapping_, file_name_); mapping_base_ = nullptr; - mapping_ = nullptr; + mapping_ = xe::memory::kFileMappingHandleInvalid; } virtual_membase_ = nullptr; @@ -125,8 +125,7 @@ Memory::~Memory() { } bool Memory::Initialize() { - file_name_ = - fmt::format("Local\\xenia_memory_{}", Clock::QueryHostTickCount()); + file_name_ = fmt::format("xenia_memory_{}", Clock::QueryHostTickCount()); // Create main page file-backed mapping. This is all reserved but // uncommitted (so it shouldn't expand page file). @@ -134,9 +133,9 @@ bool Memory::Initialize() { file_name_, // entire 4gb space + 512mb physical: 0x11FFFFFFF, xe::memory::PageAccess::kReadWrite, false); - if (!mapping_) { + if (mapping_ == xe::memory::kFileMappingHandleInvalid) { XELOGE("Unable to reserve the 4gb guest address space."); - assert_not_null(mapping_); + assert_always(); return false; } diff --git a/src/xenia/memory.h b/src/xenia/memory.h index fa0c62f4c..df76c39bc 100644 --- a/src/xenia/memory.h +++ b/src/xenia/memory.h @@ -491,7 +491,8 @@ class Memory { uint8_t* virtual_membase_ = nullptr; uint8_t* physical_membase_ = nullptr; - xe::memory::FileMappingHandle mapping_ = nullptr; + xe::memory::FileMappingHandle mapping_ = + xe::memory::kFileMappingHandleInvalid; uint8_t* mapping_base_ = nullptr; union { struct {