Skip to content

Commit 860b1e6

Browse files
timblechmannaganea
andauthored
Windows: use EcoQoS for ThreadPriority::Background (#148797)
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. --------- Co-authored-by: Alexandre Ganea <[email protected]>
1 parent 3b66d4a commit 860b1e6

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

llvm/include/llvm/Support/Windows/WindowsSupport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ LLVM_ABI std::error_code widenPath(const Twine &Path8,
245245
SmallVectorImpl<wchar_t> &Path16,
246246
size_t MaxPathLen = MAX_PATH);
247247

248+
/// Retrieves the handle to a in-memory system module such as ntdll.dll, while
249+
/// ensuring we're not retrieving a malicious injected module but a module
250+
/// loaded from the system path.
251+
LLVM_ABI HMODULE loadSystemModuleSecure(LPCWSTR lpModuleName);
248252
} // end namespace windows
249253
} // end namespace sys
250254
} // end namespace llvm.

llvm/lib/Support/Windows/Threading.inc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,67 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) {
106106
Name.clear();
107107
}
108108

109+
namespace llvm::sys::windows {
110+
HMODULE loadSystemModuleSecure(LPCWSTR lpModuleName) {
111+
// Ensure we load indeed a module from system32 path.
112+
// As per GetModuleHandle documentation:
113+
// "If lpModuleName does not include a path and there is more than one loaded
114+
// module with the same base name and extension, you cannot predict which
115+
// module handle will be returned.". This mitigates
116+
// https://learn.microsoft.com/en-us/security-updates/securityadvisories/2010/2269637
117+
SmallVector<wchar_t, MAX_PATH> Buf;
118+
size_t Size = MAX_PATH;
119+
do {
120+
Buf.resize_for_overwrite(Size);
121+
SetLastError(NO_ERROR);
122+
Size = ::GetSystemDirectoryW(Buf.data(), Buf.size());
123+
if (Size == 0)
124+
return NULL;
125+
126+
// Try again with larger buffer.
127+
} while (Size > Buf.size());
128+
129+
Buf.truncate(Size);
130+
Buf.push_back(L'\\');
131+
Buf.append(lpModuleName, lpModuleName + std::wcslen(lpModuleName));
132+
Buf.push_back(0);
133+
134+
return ::GetModuleHandleW(Buf.data());
135+
}
136+
} // namespace llvm::sys::windows
137+
109138
SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) {
139+
HMODULE kernelM = llvm::sys::windows::loadSystemModuleSecure(L"kernel32.dll");
140+
if (kernelM) {
141+
// SetThreadInformation is only available on Windows 8 and later. Since we
142+
// still support compilation on Windows 7, we load the function dynamically.
143+
typedef BOOL(WINAPI * SetThreadInformation_t)(
144+
HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass,
145+
_In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation,
146+
ULONG ThreadInformationSize);
147+
static const auto pfnSetThreadInformation =
148+
(SetThreadInformation_t)::GetProcAddress(kernelM,
149+
"SetThreadInformation");
150+
if (pfnSetThreadInformation) {
151+
auto setThreadInformation = [](ULONG ControlMaskAndStateMask) {
152+
THREAD_POWER_THROTTLING_STATE state{};
153+
state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
154+
state.ControlMask = ControlMaskAndStateMask;
155+
state.StateMask = ControlMaskAndStateMask;
156+
return pfnSetThreadInformation(
157+
::GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state));
158+
};
159+
160+
// Use EcoQoS for ThreadPriority::Background available (running on most
161+
// efficent cores at the most efficient cpu frequency):
162+
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation
163+
// https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
164+
setThreadInformation(Priority == ThreadPriority::Background
165+
? THREAD_POWER_THROTTLING_EXECUTION_SPEED
166+
: 0);
167+
}
168+
}
169+
110170
// https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority
111171
// Begin background processing mode. The system lowers the resource scheduling
112172
// priorities of the thread so that it can perform background work without

0 commit comments

Comments
 (0)