graphics: pause memory-tracking spin loops
This commit is contained in:
parent
a87889f032
commit
006875ed4f
|
|
@ -15,6 +15,12 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#include <intrin.h>
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
|
|
@ -62,7 +68,13 @@ class SpinGuard final {
|
||||||
public:
|
public:
|
||||||
explicit SpinGuard(std::atomic_flag& lock): m_lock(lock) {
|
explicit SpinGuard(std::atomic_flag& lock): m_lock(lock) {
|
||||||
while (m_lock.test_and_set(std::memory_order_acquire)) {
|
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); }
|
~SpinGuard() { m_lock.clear(std::memory_order_release); }
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,12 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#include <intrin.h>
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
|
|
@ -32,7 +38,13 @@ public:
|
||||||
const auto thread = CurrentThread();
|
const auto thread = CurrentThread();
|
||||||
while (m_lock.test_and_set(std::memory_order_acquire)) {
|
while (m_lock.test_and_set(std::memory_order_acquire)) {
|
||||||
EXIT_NOT_IMPLEMENTED(m_owner.load(std::memory_order_relaxed) == thread);
|
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);
|
m_owner.store(thread, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
#include "common/virtualMemory.h"
|
#include "common/virtualMemory.h"
|
||||||
#include "graphics/host_gpu/pageManager.h"
|
#include "graphics/host_gpu/pageManager.h"
|
||||||
|
|
||||||
|
#include <barrier>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
|
|
@ -167,6 +170,7 @@ struct ProtectionCall {
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
};
|
};
|
||||||
std::vector<ProtectionCall> g_protection_ranges;
|
std::vector<ProtectionCall> g_protection_ranges;
|
||||||
|
std::mutex g_protection_log_mutex;
|
||||||
|
|
||||||
bool ProtectAddressSpace(uint64_t vaddr, uint64_t size,
|
bool ProtectAddressSpace(uint64_t vaddr, uint64_t size,
|
||||||
Common::VirtualMemory::Mode mode) {
|
Common::VirtualMemory::Mode mode) {
|
||||||
|
|
@ -177,8 +181,11 @@ bool ProtectAddressSpace(uint64_t vaddr, uint64_t size,
|
||||||
protection = PAGE_READWRITE;
|
protection = PAGE_READWRITE;
|
||||||
}
|
}
|
||||||
DWORD old_protection = 0;
|
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,
|
return VirtualProtect(reinterpret_cast<void *>(vaddr), size, protection,
|
||||||
&old_protection) != 0;
|
&old_protection) != 0;
|
||||||
}
|
}
|
||||||
|
|
@ -547,6 +554,34 @@ void TestReadWriteWatcherInteractions() {
|
||||||
Check(VirtualFree(memory, 0, MEM_RELEASE) != 0, "VirtualFree failed");
|
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) {
|
[[noreturn]] void RunDeathCase(const char *name) {
|
||||||
PageManager manager;
|
PageManager manager;
|
||||||
const auto page_size = manager.GetPageSize();
|
const auto page_size = manager.GetPageSize();
|
||||||
|
|
@ -669,6 +704,7 @@ int main(int argc, char **argv) {
|
||||||
TestRegionMaskWatcherRanges();
|
TestRegionMaskWatcherRanges();
|
||||||
TestRegionEndpointBatching();
|
TestRegionEndpointBatching();
|
||||||
TestReadWriteWatcherInteractions();
|
TestReadWriteWatcherInteractions();
|
||||||
|
TestConcurrentSharedWatchers();
|
||||||
TestFatalPaths();
|
TestFatalPaths();
|
||||||
std::puts("PageManagerTests: all cases passed");
|
std::puts("PageManagerTests: all cases passed");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue