[llvm] Windows: use EcoQoS for ThreadPriority::Background (PR #148797)

Alexandre Ganea via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 29 07:57:26 PDT 2025


https://github.com/aganea updated https://github.com/llvm/llvm-project/pull/148797

>From 5c586bf7478b7041dda53bfb921f4bdd514f1cf5 Mon Sep 17 00:00:00 2001
From: Tim Blechmann <tim at klingt.org>
Date: Tue, 15 Jul 2025 15:32:15 +0800
Subject: [PATCH 1/5] Windows: use EcoQoS for ThreadPriority::Background

The SetThreadInformation API allows threads to be scheduled on the most
efficient cores on the most efficient frequency.
Using this API for ThreadPriority::Background should make clangd-based
IDEs a little less CPU hungry.
---
 llvm/lib/Support/Windows/Threading.inc | 29 ++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc
index d862dbd7f71c9..571e0960a6888 100644
--- a/llvm/lib/Support/Windows/Threading.inc
+++ b/llvm/lib/Support/Windows/Threading.inc
@@ -107,6 +107,35 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
 }
 
 SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
+
+  // SetThreadInformation is only available on Windows 8 and later. Since we still
+  // support compilation on Windows 7, we load the function dynamically.
+  typedef BOOL(WINAPI * SetThreadInformation_t)(
+      HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass,
+      _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation,
+      ULONG ThreadInformationSize);
+  static const auto pfnSetThreadInformation =
+      (SetThreadInformation_t)GetProcAddress(
+          GetModuleHandle(TEXT("kernel32.dll")), "SetThreadInformation");
+
+  if (pfnSetThreadInformation) {
+    auto setThreadInformation = [](ULONG ControlMaskAndStateMask) {
+      THREAD_POWER_THROTTLING_STATE state{};
+      state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
+      state.ControlMask = ControlMaskAndStateMask;
+      state.StateMask = ControlMaskAndStateMask;
+      return pfnSetThreadInformation(GetCurrentThread(), ThreadPowerThrottling,
+                                     &state, sizeof(state));
+    };
+
+    // Use EcoQoS for ThreadPriority::Background available (running on most
+    // efficent cores at the most efficient cpu frequency):
+    // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation
+    // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
+    setThreadInformation(Priority == ThreadPriority::Background ? THREAD_POWER_THROTTLING_EXECUTION_SPEED
+                                                                : 0);
+  }
+
   // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority
   // Begin background processing mode. The system lowers the resource scheduling
   // priorities of the thread so that it can perform background work without

>From 00e16ce021e2297e1bc3b417ead2e0e0fdc42139 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Wed, 23 Jul 2025 16:36:04 -0400
Subject: [PATCH 2/5] clang-format

---
 llvm/lib/Support/Windows/Threading.inc | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc
index 571e0960a6888..49a7974d312af 100644
--- a/llvm/lib/Support/Windows/Threading.inc
+++ b/llvm/lib/Support/Windows/Threading.inc
@@ -107,9 +107,8 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
 }
 
 SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
-
-  // SetThreadInformation is only available on Windows 8 and later. Since we still
-  // support compilation on Windows 7, we load the function dynamically.
+  // SetThreadInformation is only available on Windows 8 and later. Since we
+  // still support compilation on Windows 7, we load the function dynamically.
   typedef BOOL(WINAPI * SetThreadInformation_t)(
       HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass,
       _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation,
@@ -132,8 +131,9 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
     // efficent cores at the most efficient cpu frequency):
     // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation
     // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
-    setThreadInformation(Priority == ThreadPriority::Background ? THREAD_POWER_THROTTLING_EXECUTION_SPEED
-                                                                : 0);
+    setThreadInformation(Priority == ThreadPriority::Background
+                             ? THREAD_POWER_THROTTLING_EXECUTION_SPEED
+                             : 0);
   }
 
   // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority

>From 8130f416a1135ff905c9f210197cd6c844f676c0 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Wed, 23 Jul 2025 17:37:31 -0400
Subject: [PATCH 3/5] Ensure we load the system kernel32.dll and not another
 injected lib bearing the same name

---
 llvm/lib/Support/Windows/Threading.inc | 84 +++++++++++++++++---------
 1 file changed, 57 insertions(+), 27 deletions(-)

diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc
index 49a7974d312af..2d295d1c07ee8 100644
--- a/llvm/lib/Support/Windows/Threading.inc
+++ b/llvm/lib/Support/Windows/Threading.inc
@@ -106,34 +106,64 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
   Name.clear();
 }
 
-SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
-  // SetThreadInformation is only available on Windows 8 and later. Since we
-  // still support compilation on Windows 7, we load the function dynamically.
-  typedef BOOL(WINAPI * SetThreadInformation_t)(
-      HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass,
-      _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation,
-      ULONG ThreadInformationSize);
-  static const auto pfnSetThreadInformation =
-      (SetThreadInformation_t)GetProcAddress(
-          GetModuleHandle(TEXT("kernel32.dll")), "SetThreadInformation");
-
-  if (pfnSetThreadInformation) {
-    auto setThreadInformation = [](ULONG ControlMaskAndStateMask) {
-      THREAD_POWER_THROTTLING_STATE state{};
-      state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
-      state.ControlMask = ControlMaskAndStateMask;
-      state.StateMask = ControlMaskAndStateMask;
-      return pfnSetThreadInformation(GetCurrentThread(), ThreadPowerThrottling,
-                                     &state, sizeof(state));
-    };
+static HMODULE LoadSystemModuleSecure(LPCWSTR lpModuleName) {
+  // Ensure we load indeed a module from system32 path.
+  // As per GetModuleHandle documentation:
+  // "If lpModuleName does not include a path and there is more than one loaded
+  // module with the same base name and extension, you cannot predict which
+  // module handle will be returned.". This mitigates
+  // https://learn.microsoft.com/en-us/security-updates/securityadvisories/2010/2269637
+  SmallVector<wchar_t, MAX_PATH> Buf;
+  size_t Size = MAX_PATH;
+  do {
+    Buf.resize_for_overwrite(Size);
+    SetLastError(NO_ERROR);
+    Size = ::GetSystemDirectoryW(Buf.data(), Buf.size());
+    if (Size == 0)
+      return NULL;
+
+    // Try again with larger buffer.
+  } while (Size > Buf.size());
+
+  Buf.truncate(Size);
+  Buf.push_back(TEXT('\\'));
+  Buf.append(lpModuleName, lpModuleName + std::wcslen(lpModuleName));
+  Buf.push_back(0);
+
+  return GetModuleHandleW(Buf.data());
+}
 
-    // Use EcoQoS for ThreadPriority::Background available (running on most
-    // efficent cores at the most efficient cpu frequency):
-    // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation
-    // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
-    setThreadInformation(Priority == ThreadPriority::Background
-                             ? THREAD_POWER_THROTTLING_EXECUTION_SPEED
-                             : 0);
+SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
+  HMODULE kernelMod = LoadSystemModuleSecure(TEXT("kernel32.dll"));
+  if (kernelMod) {
+    // SetThreadInformation is only available on Windows 8 and later. Since we
+    // still support compilation on Windows 7, we load the function dynamically.
+    typedef BOOL(WINAPI * SetThreadInformation_t)(
+        HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass,
+        _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation,
+        ULONG ThreadInformationSize);
+    static const auto pfnSetThreadInformation =
+        (SetThreadInformation_t)GetProcAddress(kernelMod,
+                                               "SetThreadInformation");
+
+    if (pfnSetThreadInformation) {
+      auto setThreadInformation = [](ULONG ControlMaskAndStateMask) {
+        THREAD_POWER_THROTTLING_STATE state{};
+        state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
+        state.ControlMask = ControlMaskAndStateMask;
+        state.StateMask = ControlMaskAndStateMask;
+        return pfnSetThreadInformation(
+            GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state));
+      };
+
+      // Use EcoQoS for ThreadPriority::Background available (running on most
+      // efficent cores at the most efficient cpu frequency):
+      // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation
+      // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
+      setThreadInformation(Priority == ThreadPriority::Background
+                               ? THREAD_POWER_THROTTLING_EXECUTION_SPEED
+                               : 0);
+    }
   }
 
   // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority

>From 670a81a8220b5d37d5ed083a5d868683b1657fed Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Wed, 23 Jul 2025 17:44:25 -0400
Subject: [PATCH 4/5] Use global functions

---
 llvm/lib/Support/Windows/Threading.inc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc
index 2d295d1c07ee8..5ef79b340caf1 100644
--- a/llvm/lib/Support/Windows/Threading.inc
+++ b/llvm/lib/Support/Windows/Threading.inc
@@ -130,7 +130,7 @@ static HMODULE LoadSystemModuleSecure(LPCWSTR lpModuleName) {
   Buf.append(lpModuleName, lpModuleName + std::wcslen(lpModuleName));
   Buf.push_back(0);
 
-  return GetModuleHandleW(Buf.data());
+  return ::GetModuleHandleW(Buf.data());
 }
 
 SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
@@ -143,8 +143,8 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
         _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation,
         ULONG ThreadInformationSize);
     static const auto pfnSetThreadInformation =
-        (SetThreadInformation_t)GetProcAddress(kernelMod,
-                                               "SetThreadInformation");
+        (SetThreadInformation_t)::GetProcAddress(kernelMod,
+                                                 "SetThreadInformation");
 
     if (pfnSetThreadInformation) {
       auto setThreadInformation = [](ULONG ControlMaskAndStateMask) {
@@ -153,7 +153,7 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
         state.ControlMask = ControlMaskAndStateMask;
         state.StateMask = ControlMaskAndStateMask;
         return pfnSetThreadInformation(
-            GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state));
+            ::GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state));
       };
 
       // Use EcoQoS for ThreadPriority::Background available (running on most

>From c37d94c94323a419123fae5521c2c87994117e7e Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <alex_toresh at yahoo.fr>
Date: Sun, 27 Jul 2025 13:59:42 -0400
Subject: [PATCH 5/5] Force wchar_t string; move function to WindowsSupport.h

---
 llvm/include/llvm/Support/Windows/WindowsSupport.h |  4 ++++
 llvm/lib/Support/Windows/Threading.inc             | 13 +++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/llvm/include/llvm/Support/Windows/WindowsSupport.h b/llvm/include/llvm/Support/Windows/WindowsSupport.h
index ffc6fdf5b983c..f35e7b55cb8d3 100644
--- a/llvm/include/llvm/Support/Windows/WindowsSupport.h
+++ b/llvm/include/llvm/Support/Windows/WindowsSupport.h
@@ -245,6 +245,10 @@ LLVM_ABI std::error_code widenPath(const Twine &Path8,
                                    SmallVectorImpl<wchar_t> &Path16,
                                    size_t MaxPathLen = MAX_PATH);
 
+/// Retrieves the handle to a in-memory system module such as ntdll.dll, while
+/// ensuring we're not retrieving a malicious injected module but a module
+/// loaded from the system path.
+LLVM_ABI HMODULE loadSystemModuleSecure(LPCWSTR lpModuleName);
 } // end namespace windows
 } // end namespace sys
 } // end namespace llvm.
diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc
index 5ef79b340caf1..8dd7c88fad34a 100644
--- a/llvm/lib/Support/Windows/Threading.inc
+++ b/llvm/lib/Support/Windows/Threading.inc
@@ -106,7 +106,8 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
   Name.clear();
 }
 
-static HMODULE LoadSystemModuleSecure(LPCWSTR lpModuleName) {
+namespace llvm::sys::windows {
+HMODULE loadSystemModuleSecure(LPCWSTR lpModuleName) {
   // Ensure we load indeed a module from system32 path.
   // As per GetModuleHandle documentation:
   // "If lpModuleName does not include a path and there is more than one loaded
@@ -126,16 +127,17 @@ static HMODULE LoadSystemModuleSecure(LPCWSTR lpModuleName) {
   } while (Size > Buf.size());
 
   Buf.truncate(Size);
-  Buf.push_back(TEXT('\\'));
+  Buf.push_back(L'\\');
   Buf.append(lpModuleName, lpModuleName + std::wcslen(lpModuleName));
   Buf.push_back(0);
 
   return ::GetModuleHandleW(Buf.data());
 }
+} // namespace llvm::sys::windows
 
 SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
-  HMODULE kernelMod = LoadSystemModuleSecure(TEXT("kernel32.dll"));
-  if (kernelMod) {
+  HMODULE kernelM = llvm::sys::windows::loadSystemModuleSecure(L"kernel32.dll");
+  if (kernelM) {
     // SetThreadInformation is only available on Windows 8 and later. Since we
     // still support compilation on Windows 7, we load the function dynamically.
     typedef BOOL(WINAPI * SetThreadInformation_t)(
@@ -143,9 +145,8 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
         _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation,
         ULONG ThreadInformationSize);
     static const auto pfnSetThreadInformation =
-        (SetThreadInformation_t)::GetProcAddress(kernelMod,
+        (SetThreadInformation_t)::GetProcAddress(kernelM,
                                                  "SetThreadInformation");
-
     if (pfnSetThreadInformation) {
       auto setThreadInformation = [](ULONG ControlMaskAndStateMask) {
         THREAD_POWER_THROTTLING_STATE state{};



More information about the llvm-commits mailing list