From 006875ed4f223283e50c70cd1cd03791e6a3ded6 Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 7 Oct 2026 22:27:36 +0400 Subject: [PATCH] graphics: pause memory-tracking spin loops --- src/graphics/host_gpu/pageManager.cpp | 14 +++++++++- src/graphics/host_gpu/regionManager.h | 14 +++++++++- tests/PageManagerTests.cpp | 40 +++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/graphics/host_gpu/pageManager.cpp b/src/graphics/host_gpu/pageManager.cpp index 00debbbd..75829739 100644 --- a/src/graphics/host_gpu/pageManager.cpp +++ b/src/graphics/host_gpu/pageManager.cpp @@ -15,6 +15,12 @@ #include #include +#if defined(_MSC_VER) +#include +#elif defined(__x86_64__) +#include +#endif + #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS #ifndef NOMINMAX #define NOMINMAX @@ -62,7 +68,13 @@ class SpinGuard final { public: explicit SpinGuard(std::atomic_flag& lock): m_lock(lock) { while (m_lock.test_and_set(std::memory_order_acquire)) { - std::atomic_signal_fence(std::memory_order_seq_cst); +#if defined(__x86_64__) || defined(_M_X64) + _mm_pause(); +#elif defined(_M_ARM64) + __yield(); +#elif defined(__aarch64__) + asm volatile("yield"); +#endif } } ~SpinGuard() { m_lock.clear(std::memory_order_release); } diff --git a/src/graphics/host_gpu/regionManager.h b/src/graphics/host_gpu/regionManager.h index 4c95cc2c..9fae4ef7 100644 --- a/src/graphics/host_gpu/regionManager.h +++ b/src/graphics/host_gpu/regionManager.h @@ -9,6 +9,12 @@ #include #include +#if defined(_MSC_VER) +#include +#elif defined(__x86_64__) +#include +#endif + #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS #ifndef NOMINMAX #define NOMINMAX @@ -32,7 +38,13 @@ public: const auto thread = CurrentThread(); while (m_lock.test_and_set(std::memory_order_acquire)) { EXIT_NOT_IMPLEMENTED(m_owner.load(std::memory_order_relaxed) == thread); - std::atomic_signal_fence(std::memory_order_seq_cst); +#if defined(__x86_64__) || defined(_M_X64) + _mm_pause(); +#elif defined(_M_ARM64) + __yield(); +#elif defined(__aarch64__) + asm volatile("yield"); +#endif } m_owner.store(thread, std::memory_order_relaxed); } diff --git a/tests/PageManagerTests.cpp b/tests/PageManagerTests.cpp index effb8c95..fe13707f 100644 --- a/tests/PageManagerTests.cpp +++ b/tests/PageManagerTests.cpp @@ -1,12 +1,15 @@ #include "common/virtualMemory.h" #include "graphics/host_gpu/pageManager.h" +#include #include #include #include #include #include +#include #include +#include #include #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS @@ -167,6 +170,7 @@ struct ProtectionCall { uint64_t size; }; std::vector g_protection_ranges; +std::mutex g_protection_log_mutex; bool ProtectAddressSpace(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode) { @@ -177,8 +181,11 @@ bool ProtectAddressSpace(uint64_t vaddr, uint64_t size, protection = PAGE_READWRITE; } DWORD old_protection = 0; - g_protection_calls++; - g_protection_ranges.push_back({vaddr, size}); + { + std::lock_guard lock(g_protection_log_mutex); + g_protection_calls++; + g_protection_ranges.push_back({vaddr, size}); + } return VirtualProtect(reinterpret_cast(vaddr), size, protection, &old_protection) != 0; } @@ -547,6 +554,34 @@ void TestReadWriteWatcherInteractions() { Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed"); } +void TestConcurrentSharedWatchers() { + constexpr size_t worker_count = 8; + PageManager manager; + auto *memory = Allocate(TRACKER_PAGE_SIZE); + const auto address = reinterpret_cast(memory); + bool watched = false; + std::barrier phase(worker_count, [&]() noexcept { + watched = !watched; + Check(Protection(memory) == (watched ? PAGE_READONLY : PAGE_READWRITE), + "concurrent watchers lost protection or failed to restore writes"); + }); + std::vector workers; + for (size_t i = 0; i < worker_count; i++) { + workers.emplace_back([&] { + for (size_t round = 0; round < 100; round++) { + manager.UpdatePageWatchers(address, TRACKER_PAGE_SIZE); + phase.arrive_and_wait(); + manager.UpdatePageWatchers(address, TRACKER_PAGE_SIZE); + phase.arrive_and_wait(); + } + }); + } + for (auto &worker : workers) { + worker.join(); + } + Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed"); +} + [[noreturn]] void RunDeathCase(const char *name) { PageManager manager; const auto page_size = manager.GetPageSize(); @@ -669,6 +704,7 @@ int main(int argc, char **argv) { TestRegionMaskWatcherRanges(); TestRegionEndpointBatching(); TestReadWriteWatcherInteractions(); + TestConcurrentSharedWatchers(); TestFatalPaths(); std::puts("PageManagerTests: all cases passed"); return 0;