From c3fb6ccaaa8667e8bf822055895f54ba02e4ac34 Mon Sep 17 00:00:00 2001 From: "Pierre-Loup A. Griffais" Date: Sun, 23 Aug 2026 08:16:16 -0700 Subject: [PATCH] DiskCache: make Writer thread low-priority --- FEXCore/Source/Interface/Core/DiskCache.cpp | 2 +- FEXCore/Source/Utils/Threads.cpp | 6 +++--- FEXCore/Source/Utils/WorkQueueThread.cpp | 4 ++-- FEXCore/include/FEXCore/Utils/Threads.h | 4 ++-- .../include/FEXCore/Utils/WorkQueueThread.h | 2 +- .../LinuxSyscalls/Utils/Threads.cpp | 20 +++++++++++++++---- Source/Windows/Common/Threads.cpp | 18 +++++++++++++---- 7 files changed, 39 insertions(+), 17 deletions(-) diff --git a/FEXCore/Source/Interface/Core/DiskCache.cpp b/FEXCore/Source/Interface/Core/DiskCache.cpp index 776cabbf7..ac3623c95 100644 --- a/FEXCore/Source/Interface/Core/DiskCache.cpp +++ b/FEXCore/Source/Interface/Core/DiskCache.cpp @@ -316,7 +316,7 @@ namespace DiskCache { } if (IsWritingDiskCache()) { - Writer = fextl::make_unique(); + Writer = fextl::make_unique(true); } } diff --git a/FEXCore/Source/Utils/Threads.cpp b/FEXCore/Source/Utils/Threads.cpp index cceb3d95c..fd0701563 100644 --- a/FEXCore/Source/Utils/Threads.cpp +++ b/FEXCore/Source/Utils/Threads.cpp @@ -7,7 +7,7 @@ #include namespace FEXCore::Threads { -static fextl::unique_ptr CreateThread_Default(ThreadFunc Func, void* Arg) { +static fextl::unique_ptr CreateThread_Default(ThreadFunc Func, void* Arg, bool LowPriority) { ERROR_AND_DIE_FMT("Frontend didn't setup thread creation!"); } @@ -20,8 +20,8 @@ static FEXCore::Threads::Pointers Ptrs = { .CleanupAfterFork = CleanupAfterFork_Default, }; -fextl::unique_ptr FEXCore::Threads::Thread::Create(ThreadFunc Func, void* Arg) { - return Ptrs.CreateThread(Func, Arg); +fextl::unique_ptr FEXCore::Threads::Thread::Create(ThreadFunc Func, void* Arg, bool LowPriority) { + return Ptrs.CreateThread(Func, Arg, LowPriority); } void FEXCore::Threads::Thread::CleanupAfterFork() { diff --git a/FEXCore/Source/Utils/WorkQueueThread.cpp b/FEXCore/Source/Utils/WorkQueueThread.cpp index 1ca2eccfb..2d3a74f15 100644 --- a/FEXCore/Source/Utils/WorkQueueThread.cpp +++ b/FEXCore/Source/Utils/WorkQueueThread.cpp @@ -4,8 +4,8 @@ namespace FEXCore { -WorkQueueThread::WorkQueueThread() { - Thread = FEXCore::Threads::Thread::Create(ThreadEntry, this); +WorkQueueThread::WorkQueueThread(bool LowPriority) { + Thread = FEXCore::Threads::Thread::Create(ThreadEntry, this, LowPriority); } WorkQueueThread::~WorkQueueThread() { diff --git a/FEXCore/include/FEXCore/Utils/Threads.h b/FEXCore/include/FEXCore/Utils/Threads.h index a4e924147..43616c613 100644 --- a/FEXCore/include/FEXCore/Utils/Threads.h +++ b/FEXCore/include/FEXCore/Utils/Threads.h @@ -7,7 +7,7 @@ namespace FEXCore::Threads { using ThreadFunc = void* (*)(void* user_ptr); class Thread; -using CreateThreadFunc = fextl::unique_ptr (*)(ThreadFunc Func, void* Arg); +using CreateThreadFunc = fextl::unique_ptr (*)(ThreadFunc Func, void* Arg, bool LowPriority); using CleanupAfterForkFunc = void (*)(); struct Pointers { @@ -28,7 +28,7 @@ public: * @name Calls provided API functions * @{ */ - static fextl::unique_ptr Create(ThreadFunc Func, void* Arg); + static fextl::unique_ptr Create(ThreadFunc Func, void* Arg, bool LowPriority = false); static void CleanupAfterFork(); diff --git a/FEXCore/include/FEXCore/Utils/WorkQueueThread.h b/FEXCore/include/FEXCore/Utils/WorkQueueThread.h index e9ff114a9..a17d3d7ce 100644 --- a/FEXCore/include/FEXCore/Utils/WorkQueueThread.h +++ b/FEXCore/include/FEXCore/Utils/WorkQueueThread.h @@ -18,7 +18,7 @@ public: virtual void Run() = 0; }; - WorkQueueThread(); + WorkQueueThread(bool LowPriority = false); ~WorkQueueThread(); void QueueWork(fextl::unique_ptr Work); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Utils/Threads.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Utils/Threads.cpp index 080afc297..34b6750f6 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Utils/Threads.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Utils/Threads.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include namespace FEX::LinuxEmulation::Threads { void* StackTracker::AllocateStackObject() { @@ -195,10 +197,11 @@ namespace PThreads { class PThread final : public FEXCore::Threads::Thread { public: - PThread(StackTracker* STracker, FEXCore::Threads::ThreadFunc Func, void* Arg) + PThread(StackTracker* STracker, FEXCore::Threads::ThreadFunc Func, void* Arg, bool LowPriority) : STracker {STracker} , UserFunc {Func} - , UserArg {Arg} { + , UserArg {Arg} + , LowPriority {LowPriority} { pthread_attr_t Attr {}; Stack = STracker->AllocateStackObject(); // pthreads allocates its dtv region behind our back and there is nothing we can do about it. @@ -257,6 +260,10 @@ namespace PThreads { return Stack; } + bool GetLowPriority() const { + return LowPriority; + } + StackTracker* GetStackTracker() const { return STracker; } @@ -287,6 +294,7 @@ namespace PThreads { FEXCore::Threads::ThreadFunc UserFunc; void* UserArg; void* Stack {}; + bool LowPriority {}; // Use FEXCore's UncheckedLongJump to avoid fortification checks. // This avoids a false positive since glibc does not understand stack pivots. @@ -305,6 +313,10 @@ namespace PThreads { bool LongJumpExit {}; + if (Thread->GetLowPriority()) { + setpriority(PRIO_PROCESS, FHU::Syscalls::gettid(), 19); + } + if (FEXCore::UncheckedLongJump::SetJump(exit_resolver) == 0) { Thread->SetupLongJump(&exit_resolver); // Run the user function. @@ -354,8 +366,8 @@ namespace PThreads { static StackTracker* STracker {}; - static fextl::unique_ptr CreateThread_PThread(FEXCore::Threads::ThreadFunc Func, void* Arg) { - return fextl::make_unique(STracker, Func, Arg); + static fextl::unique_ptr CreateThread_PThread(FEXCore::Threads::ThreadFunc Func, void* Arg, bool LowPriority) { + return fextl::make_unique(STracker, Func, Arg, LowPriority); } static void CleanupAfterFork_PThread() { diff --git a/Source/Windows/Common/Threads.cpp b/Source/Windows/Common/Threads.cpp index 55f741334..a703caeac 100644 --- a/Source/Windows/Common/Threads.cpp +++ b/Source/Windows/Common/Threads.cpp @@ -13,9 +13,10 @@ namespace FEX::Windows { namespace WinThreadImpl { class Thread final : public FEXCore::Threads::Thread { public: - Thread(FEXCore::Threads::ThreadFunc Func, void* Arg) + Thread(FEXCore::Threads::ThreadFunc Func, void* Arg, bool LowPriority) : UserFunc {Func} - , UserArg {Arg} { + , UserArg {Arg} + , LowPriority {LowPriority} { // hide everything from guest, don't initialize anything, we'll do that manually in RunThread() const ULONG CreateFlags = THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER | THREAD_CREATE_FLAGS_SKIP_LOADER_INIT | THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE; @@ -61,9 +62,17 @@ namespace WinThreadImpl { } } + bool GetLowPriority() const { + return LowPriority; + } + private: static void RunThread(Thread* This) { This->TID = GetCurrentThreadId(); + if (This->GetLowPriority()) { + LONG Priority = THREAD_BASE_PRIORITY_IDLE; + NtSetInformationThread(NtCurrentThread(), ThreadBasePriority, &Priority, sizeof(Priority)); + } // do initialization we skipped earlier here around the user entrypoint FEX::Windows::InitCRTThread(); This->ReturnValue = This->UserFunc(This->UserArg); @@ -73,13 +82,14 @@ namespace WinThreadImpl { FEXCore::Threads::ThreadFunc UserFunc; void* UserArg; + bool LowPriority {}; HANDLE Handle {}; DWORD TID {}; void* ReturnValue {}; }; - fextl::unique_ptr CreateThread(FEXCore::Threads::ThreadFunc Func, void* Arg) { - return fextl::make_unique(Func, Arg); + fextl::unique_ptr CreateThread(FEXCore::Threads::ThreadFunc Func, void* Arg, bool LowPriority) { + return fextl::make_unique(Func, Arg, LowPriority); } void CleanupAfterFork() {}