mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-11-08 10:29:00 +00:00
Removes dynamic loading on Windows
All required functions are available on all supported Windows versions.
This commit is contained in:
6 files changed
+42
-137
No files matched your search
@@ -115,19 +115,11 @@ size_t os_cpu_LargePageSize()
|
||||
|
||||
if(largePageSize == ~(size_t)0)
|
||||
{
|
||||
WUTIL_FUNC(pGetLargePageMinimum, SIZE_T, (void));
|
||||
WUTIL_IMPORT_KERNEL32(GetLargePageMinimum, pGetLargePageMinimum);
|
||||
if(pGetLargePageMinimum)
|
||||
{
|
||||
largePageSize = pGetLargePageMinimum();
|
||||
// Note: checks disabled due to failing on Vista SP2 with old Xeon CPU
|
||||
// see https://gitea.wildfiregames.com/0ad/0ad/issues/2346
|
||||
//ENSURE(largePageSize != 0); // IA-32 and AMD64 definitely support large pages
|
||||
//ENSURE(largePageSize > os_cpu_PageSize());
|
||||
}
|
||||
// no OS support for large pages
|
||||
else
|
||||
largePageSize = 0;
|
||||
largePageSize = GetLargePageMinimum();
|
||||
// Note: checks disabled due to failing on Vista SP2 with old Xeon CPU
|
||||
// see https://gitea.wildfiregames.com/0ad/0ad/issues/2346
|
||||
//ENSURE(largePageSize != 0); // IA-32 and AMD64 definitely support large pages
|
||||
//ENSURE(largePageSize > os_cpu_PageSize());
|
||||
}
|
||||
|
||||
return largePageSize;
|
||||
@@ -214,23 +206,7 @@ uintptr_t wcpu_ProcessorMaskFromAffinity(DWORD_PTR processAffinity, DWORD_PTR af
|
||||
|
||||
static void VerifyRunningOnCorrectProcessors(DWORD_PTR affinity)
|
||||
{
|
||||
DWORD currentProcessor;
|
||||
|
||||
// note: NtGetCurrentProcessorNumber and RtlGetCurrentProcessorNumber aren't
|
||||
// implemented on WinXP SP2.
|
||||
WUTIL_FUNC(pGetCurrentProcessorNumber, DWORD, (void));
|
||||
WUTIL_IMPORT_KERNEL32(GetCurrentProcessorNumber, pGetCurrentProcessorNumber);
|
||||
if(pGetCurrentProcessorNumber)
|
||||
currentProcessor = pGetCurrentProcessorNumber();
|
||||
else
|
||||
{
|
||||
// note: searching for the current APIC ID or IDT address in a
|
||||
// table won't work because initializing the table also requires
|
||||
// this function. LSL only works on Vista (which already
|
||||
// has GetCurrentProcessorNumber).
|
||||
return;
|
||||
}
|
||||
|
||||
const DWORD currentProcessor{GetCurrentProcessorNumber()};
|
||||
ENSURE(IsBitSet(affinity, currentProcessor));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -29,17 +29,12 @@ namespace wfirmware {
|
||||
|
||||
TableIds GetTableIDs(Provider provider)
|
||||
{
|
||||
WUTIL_FUNC(pEnumSystemFirmwareTables, UINT, (DWORD, PVOID, DWORD));
|
||||
WUTIL_IMPORT_KERNEL32(EnumSystemFirmwareTables, pEnumSystemFirmwareTables);
|
||||
if(!pEnumSystemFirmwareTables)
|
||||
return TableIds();
|
||||
|
||||
const size_t tableIdsSize = pEnumSystemFirmwareTables(provider, 0, 0);
|
||||
const size_t tableIdsSize = EnumSystemFirmwareTables(provider, 0, 0);
|
||||
ENSURE(tableIdsSize != 0);
|
||||
ENSURE(tableIdsSize % sizeof(TableId) == 0);
|
||||
TableIds tableIDs(DivideRoundUp(tableIdsSize, sizeof(TableId)), 0);
|
||||
|
||||
const size_t bytesWritten = pEnumSystemFirmwareTables(provider, &tableIDs[0], (DWORD)tableIdsSize);
|
||||
const size_t bytesWritten = EnumSystemFirmwareTables(provider, &tableIDs[0], (DWORD)tableIdsSize);
|
||||
ENSURE(bytesWritten == tableIdsSize);
|
||||
|
||||
return tableIDs;
|
||||
@@ -48,12 +43,7 @@ TableIds GetTableIDs(Provider provider)
|
||||
|
||||
Table GetTable(Provider provider, TableId tableId)
|
||||
{
|
||||
WUTIL_FUNC(pGetSystemFirmwareTable, UINT, (DWORD, DWORD, PVOID, DWORD));
|
||||
WUTIL_IMPORT_KERNEL32(GetSystemFirmwareTable, pGetSystemFirmwareTable);
|
||||
if(!pGetSystemFirmwareTable)
|
||||
return Table();
|
||||
|
||||
const size_t tableSize = pGetSystemFirmwareTable(provider, tableId, 0, 0);
|
||||
const size_t tableSize = GetSystemFirmwareTable(provider, tableId, 0, 0);
|
||||
if(tableSize == 0)
|
||||
{
|
||||
DEBUG_WARN_ERR(ERR::LOGIC);
|
||||
@@ -61,7 +51,7 @@ Table GetTable(Provider provider, TableId tableId)
|
||||
}
|
||||
|
||||
Table table(tableSize, 0);
|
||||
const size_t bytesWritten = pGetSystemFirmwareTable(provider, tableId, &table[0], (DWORD)tableSize);
|
||||
const size_t bytesWritten = GetSystemFirmwareTable(provider, tableId, &table[0], (DWORD)tableSize);
|
||||
ENSURE(bytesWritten == tableSize);
|
||||
|
||||
return table;
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "lib/sysdep/os_cpu.h"
|
||||
#include "lib/sysdep/os/win/acpi.h"
|
||||
#include "lib/sysdep/os/win/win.h"
|
||||
#include "lib/sysdep/os/win/wutil.h"
|
||||
#include "lib/sysdep/os/win/wcpu.h"
|
||||
|
||||
#include <bit>
|
||||
@@ -95,24 +94,14 @@ static Node* FindNodeWithProcessor(size_t processor)
|
||||
|
||||
static UCHAR HighestNodeNumber()
|
||||
{
|
||||
WUTIL_FUNC(pGetNumaHighestNodeNumber, BOOL, (PULONG));
|
||||
WUTIL_IMPORT_KERNEL32(GetNumaHighestNodeNumber, pGetNumaHighestNodeNumber);
|
||||
if(!pGetNumaHighestNodeNumber)
|
||||
return 0; // NUMA not supported => only one node
|
||||
|
||||
ULONG highestNodeNumber;
|
||||
const BOOL ok = pGetNumaHighestNodeNumber(&highestNodeNumber);
|
||||
ULONG highestNodeNumber{0};
|
||||
const BOOL ok = GetNumaHighestNodeNumber(&highestNodeNumber);
|
||||
WARN_IF_FALSE(ok);
|
||||
return (UCHAR)highestNodeNumber;
|
||||
}
|
||||
|
||||
static void PopulateNodes()
|
||||
{
|
||||
WUTIL_FUNC(pGetNumaNodeProcessorMask, BOOL, (UCHAR, PULONGLONG));
|
||||
WUTIL_IMPORT_KERNEL32(GetNumaNodeProcessorMask, pGetNumaNodeProcessorMask);
|
||||
if(!pGetNumaNodeProcessorMask)
|
||||
return;
|
||||
|
||||
DWORD_PTR processAffinity, systemAffinity;
|
||||
{
|
||||
const BOOL ok = GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity);
|
||||
@@ -122,9 +111,9 @@ static void PopulateNodes()
|
||||
|
||||
for(UCHAR nodeNumber = 0; nodeNumber <= HighestNodeNumber(); nodeNumber++)
|
||||
{
|
||||
ULONGLONG affinity;
|
||||
ULONGLONG affinity{0};
|
||||
{
|
||||
const BOOL ok = pGetNumaNodeProcessorMask(nodeNumber, &affinity);
|
||||
const BOOL ok = GetNumaNodeProcessorMask(nodeNumber, &affinity);
|
||||
WARN_IF_FALSE(ok);
|
||||
}
|
||||
if(!affinity)
|
||||
@@ -339,20 +328,12 @@ size_t numa_AvailableMemory(size_t node)
|
||||
// note: it is said that GetNumaAvailableMemoryNode sometimes incorrectly
|
||||
// reports zero bytes. the actual cause may however be unexpected
|
||||
// RAM configuration, e.g. not all slots filled.
|
||||
WUTIL_FUNC(pGetNumaAvailableMemoryNode, BOOL, (UCHAR, PULONGLONG));
|
||||
WUTIL_IMPORT_KERNEL32(GetNumaAvailableMemoryNode, pGetNumaAvailableMemoryNode);
|
||||
if(pGetNumaAvailableMemoryNode)
|
||||
{
|
||||
const UCHAR nodeNumber = NodeNumberFromNode(node);
|
||||
ULONGLONG availableBytes;
|
||||
const BOOL ok = pGetNumaAvailableMemoryNode(nodeNumber, &availableBytes);
|
||||
WARN_IF_FALSE(ok);
|
||||
const size_t availableMiB = size_t(availableBytes / MiB);
|
||||
return availableMiB;
|
||||
}
|
||||
// NUMA not supported - return available system memory
|
||||
else
|
||||
return os_cpu_MemoryAvailable();
|
||||
const UCHAR nodeNumber = NodeNumberFromNode(node);
|
||||
ULONGLONG availableBytes{0};
|
||||
const BOOL ok = GetNumaAvailableMemoryNode(nodeNumber, &availableBytes);
|
||||
WARN_IF_FALSE(ok);
|
||||
const size_t availableMiB = size_t(availableBytes / MiB);
|
||||
return availableMiB;
|
||||
}
|
||||
|
||||
|
||||
@@ -478,11 +459,6 @@ bool numa_IsMemoryInterleaved()
|
||||
static bool VerifyPages([[maybe_unused]] void* mem, [[maybe_unused]] size_t size,
|
||||
[[maybe_unused]] size_t pageSize, [[maybe_unused]] size_t node)
|
||||
{
|
||||
WUTIL_FUNC(pQueryWorkingSetEx, BOOL, (HANDLE, PVOID, DWORD));
|
||||
WUTIL_IMPORT_KERNEL32(QueryWorkingSetEx, pQueryWorkingSetEx);
|
||||
if(!pQueryWorkingSetEx)
|
||||
return true; // can't do anything
|
||||
|
||||
#if WINVER >= 0x600
|
||||
size_t largePageSize = os_cpu_LargePageSize();
|
||||
ENSURE(largePageSize != 0); // this value is needed for later
|
||||
@@ -492,7 +468,7 @@ static bool VerifyPages([[maybe_unused]] void* mem, [[maybe_unused]] size_t size
|
||||
PSAPI_WORKING_SET_EX_INFORMATION* wsi = new PSAPI_WORKING_SET_EX_INFORMATION[numPages];
|
||||
for(size_t i = 0; i < numPages; i++)
|
||||
wsi[i].VirtualAddress = static_cast<std::uint8_t*>(mem) + i*pageSize;
|
||||
pQueryWorkingSetEx(GetCurrentProcess(), wsi, DWORD(sizeof(PSAPI_WORKING_SET_EX_INFORMATION)*numPages));
|
||||
QueryWorkingSetEx(GetCurrentProcess(), wsi, DWORD(sizeof(PSAPI_WORKING_SET_EX_INFORMATION)*numPages));
|
||||
|
||||
// ensure each is valid and allocated on the correct node
|
||||
for(size_t i = 0; i < numPages; i++)
|
||||
|
||||
@@ -42,11 +42,10 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <ctime>
|
||||
#include <tuple>
|
||||
|
||||
// (dynamic linking preserves compatibility with previous Windows versions)
|
||||
static WUTIL_FUNC(pSetFileCompletionNotificationModes, BOOL, (HANDLE, UCHAR));
|
||||
static WUTIL_FUNC(pSetFileIoOverlappedRange, BOOL, (HANDLE, PUCHAR, ULONG));
|
||||
static WUTIL_FUNC(pSetFileValidData, BOOL, (HANDLE, LONGLONG));
|
||||
static bool useSetFileIoOverlappedRange{false};
|
||||
static bool useSetFileValidData{false};
|
||||
|
||||
// (there must be one global IOCP because aio_suspend might be called for
|
||||
// requests from different files)
|
||||
@@ -189,8 +188,8 @@ struct OvlAllocator // POD
|
||||
// pin the page in kernel address space, which means our thread
|
||||
// won't have to be the one to service the I/O, thus avoiding an APC.
|
||||
// ("thread agnostic I/O")
|
||||
if(pSetFileIoOverlappedRange)
|
||||
WARN_IF_FALSE(pSetFileIoOverlappedRange(hFile, (PUCHAR)storage, storageSize));
|
||||
if(useSetFileIoOverlappedRange)
|
||||
WARN_IF_FALSE(SetFileIoOverlappedRange(hFile, (PUCHAR)storage, storageSize));
|
||||
}
|
||||
|
||||
// @return OVERLAPPED initialized for I/O starting at offset,
|
||||
@@ -270,14 +269,9 @@ struct FileControlBlock // POD
|
||||
|
||||
AttachToCompletionPort(hFile, hIOCP, (ULONG_PTR)this);
|
||||
|
||||
// minor optimization: avoid posting to IOCP in rare cases
|
||||
// where the I/O completes synchronously
|
||||
if(pSetFileCompletionNotificationModes)
|
||||
{
|
||||
// (for reasons as yet unknown, this fails when the file is
|
||||
// opened for read-only access)
|
||||
(void)pSetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS);
|
||||
}
|
||||
// (for reasons as yet unknown, this fails when the file is
|
||||
// opened for read-only access)
|
||||
std::ignore = SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS);
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
@@ -374,17 +368,15 @@ static Status waio_Init()
|
||||
{
|
||||
fileControlBlocks.Init();
|
||||
|
||||
WUTIL_IMPORT_KERNEL32(SetFileCompletionNotificationModes, pSetFileCompletionNotificationModes);
|
||||
|
||||
// NB: using these functions when the privileges are not available would
|
||||
// trigger warnings. since callers have to check the function pointers
|
||||
// anyway, just refrain from setting them in such cases.
|
||||
|
||||
if(wutil_SetPrivilege(L"SeLockMemoryPrivilege", true) == INFO::OK)
|
||||
WUTIL_IMPORT_KERNEL32(SetFileIoOverlappedRange, pSetFileIoOverlappedRange);
|
||||
useSetFileIoOverlappedRange = true;
|
||||
|
||||
if(wutil_SetPrivilege(L"SeManageVolumePrivilege", true) == INFO::OK)
|
||||
WUTIL_IMPORT_KERNEL32(SetFileValidData, pSetFileValidData);
|
||||
useSetFileValidData = true;
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
@@ -467,7 +459,7 @@ Status waio_Preallocate(int fd, off_t size)
|
||||
}
|
||||
|
||||
// avoid synchronous zero-fill (see discussion in header)
|
||||
if(pSetFileValidData)
|
||||
if(useSetFileValidData)
|
||||
{
|
||||
// this has been reported (#849) to fail with GetLastError() == 0 for
|
||||
// both tiny and medium alignedSize, despite Administrator privileges.
|
||||
@@ -477,7 +469,7 @@ Status waio_Preallocate(int fd, off_t size)
|
||||
// verifying the filesystem is indeed FAT would be overkill; we'll just
|
||||
// ignore the return code. however, GetLastError can be used to check
|
||||
// whether other problems arose.
|
||||
(void)pSetFileValidData(hFile, alignedSize);
|
||||
std::ignore = SetFileValidData(hFile, alignedSize);
|
||||
ENSURE(GetLastError() == 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2024 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -71,9 +71,12 @@ static DWORD GetNtdllVersion()
|
||||
*/
|
||||
int uname(struct utsname* un)
|
||||
{
|
||||
WUTIL_FUNC(pRtlGetVersion, NTSTATUS, (LPOSVERSIONINFOEXW));
|
||||
WUTIL_IMPORT_NTDLL(RtlGetVersion, pRtlGetVersion);
|
||||
|
||||
// note: ntdll is guaranteed to be loaded, so we don't
|
||||
// need to LoadLibrary and FreeLibrary.
|
||||
using RtlGetVersionFunction = NTSTATUS (WINAPI*) (LPOSVERSIONINFOEXW);
|
||||
RtlGetVersionFunction pRtlGetVersion{
|
||||
reinterpret_cast<RtlGetVersionFunction>(
|
||||
GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion"))};
|
||||
if (!pRtlGetVersion)
|
||||
return -1;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2023 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -40,38 +40,6 @@ bool wutil_IsValidHandle(H h)
|
||||
return h != 0 && h != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// dynamic linking
|
||||
|
||||
// define a function pointer (optionally prepend 'static')
|
||||
#define WUTIL_FUNC(varName, ret, params)\
|
||||
ret (WINAPI* varName) params
|
||||
|
||||
// rationale:
|
||||
// - splitting up WUTIL_FUNC and WUTIL_IMPORT is a bit verbose in
|
||||
// the common case of a local function pointer definition,
|
||||
// but allows one-time initialization of static variables.
|
||||
// - differentiating between procName and varName allows searching
|
||||
// for the actual definition of the function pointer in the code.
|
||||
// - a cast would require passing in ret/params.
|
||||
// - writing a type-punned pointer breaks strict-aliasing rules.
|
||||
#define WUTIL_IMPORT(hModule, procName, varName)\
|
||||
STMT(\
|
||||
const FARPROC f = GetProcAddress(hModule, #procName);\
|
||||
memcpy(&varName, &f, sizeof(FARPROC));\
|
||||
)
|
||||
|
||||
// note: Kernel32 is guaranteed to be loaded, so we don't
|
||||
// need to LoadLibrary and FreeLibrary.
|
||||
#define WUTIL_IMPORT_KERNEL32(procName, varName)\
|
||||
WUTIL_IMPORT(GetModuleHandleW(L"kernel32.dll"), procName, varName)
|
||||
|
||||
// note: ntdll is guaranteed to be loaded, so we don't
|
||||
// need to LoadLibrary and FreeLibrary.
|
||||
#define WUTIL_IMPORT_NTDLL(procName, varName)\
|
||||
WUTIL_IMPORT(GetModuleHandleW(L"ntdll.dll"), procName, varName)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// safe allocator
|
||||
|
||||
|
||||
Reference in new issue
Block a user