graphics: pause memory-tracking spin loops
This commit is contained in:
parent
a87889f032
commit
006875ed4f
|
|
@ -15,6 +15,12 @@
|
|||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#elif defined(__x86_64__)
|
||||
#include <xmmintrin.h>
|
||||
#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); }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@
|
|||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#elif defined(__x86_64__)
|
||||
#include <xmmintrin.h>
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
#include "common/virtualMemory.h"
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
|
||||
#include <barrier>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
|
|
@ -167,6 +170,7 @@ struct ProtectionCall {
|
|||
uint64_t size;
|
||||
};
|
||||
std::vector<ProtectionCall> 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<void *>(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<uint64_t>(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<std::jthread> workers;
|
||||
for (size_t i = 0; i < worker_count; i++) {
|
||||
workers.emplace_back([&] {
|
||||
for (size_t round = 0; round < 100; round++) {
|
||||
manager.UpdatePageWatchers<true>(address, TRACKER_PAGE_SIZE);
|
||||
phase.arrive_and_wait();
|
||||
manager.UpdatePageWatchers<false>(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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue