diff --git a/source/lib/sysdep/os/bsd/bcpu.cpp b/source/lib/sysdep/os/bsd/bcpu.cpp index 6e061667ec..5eb49b3269 100644 --- a/source/lib/sysdep/os/bsd/bcpu.cpp +++ b/source/lib/sysdep/os/bsd/bcpu.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 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 @@ -110,21 +110,3 @@ size_t os_cpu_MemoryAvailable() memoryAvailable /= MiB; return memoryAvailable; } - -uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t /*processorMask*/) -{ - // not yet implemented - return os_cpu_ProcessorMask(); -} - -Status os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData) -{ - for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++) - { - const uintptr_t processorMask = uintptr_t(1) << processor; - os_cpu_SetThreadAffinityMask(processorMask); - cb(processor, cbData); - } - - return INFO::OK; -} diff --git a/source/lib/sysdep/os/linux/lcpu.cpp b/source/lib/sysdep/os/linux/lcpu.cpp index c34029446b..0ffe349cdf 100644 --- a/source/lib/sysdep/os/linux/lcpu.cpp +++ b/source/lib/sysdep/os/linux/lcpu.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 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 @@ -111,100 +111,3 @@ size_t os_cpu_MemoryAvailable() return memoryAvailable; } -#if OS_ANDROID -// the current Android NDK (r7-crystax-4) doesn't support sched_setaffinity, -// so provide a stub implementation instead - -uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t /*processorMask*/) -{ - // not yet implemented - return os_cpu_ProcessorMask(); -} - -#else - -// glibc __CPU_SETSIZE=1024 is smaller than required on some Linux (4096), -// but the CONFIG_NR_CPUS in a header may not reflect the actual kernel, -// so we have to detect the limit at runtime. -// (see https://gitea.wildfiregames.com/0ad/0ad/issues/547 for additional information) -static size_t maxCpus; - -static bool IsMaxCpusSufficient() -{ - const size_t setSize = CPU_ALLOC_SIZE(maxCpus); - cpu_set_t* set = CPU_ALLOC(maxCpus); - ENSURE(set); - const int ret = sched_getaffinity(0, setSize, set); - CPU_FREE(set); - if(ret == 0) - return true; - ENSURE(errno == EINVAL); - return false; -} - - -static Status DetectMaxCpus() -{ - // the most I have ever heard of is CONFIG_NR_CPUS=4096, - // and even that limit should be enough for years and years. - for(maxCpus = 64; maxCpus <= 65536; maxCpus *= 2) - { - if(IsMaxCpusSufficient()) - return INFO::OK; - } - return ERR::FAIL; -} - - -uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask) -{ - static ModuleInitState maxCpusInitState; - (void)ModuleInit(&maxCpusInitState, DetectMaxCpus); - const size_t setSize = CPU_ALLOC_SIZE(maxCpus); - cpu_set_t* set = CPU_ALLOC(maxCpus); - ENSURE(set); - - uintptr_t previousProcessorMask = 0; - { - int ret = sched_getaffinity(0, setSize, set); - ENSURE(ret == 0); - - for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++) - { - if(CPU_ISSET_S(processor, setSize, set)) - previousProcessorMask |= uintptr_t(1) << processor; - } - } - - CPU_ZERO_S(setSize, set); - for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++) - { - if(IsBitSet(processorMask, processor)) - CPU_SET_S(processor, setSize, set); - } - - int ret = sched_setaffinity(0, setSize, set); - ENSURE(ret == 0); - // (The process gets migrated immediately by the setaffinity call) - - CPU_FREE(set); - return previousProcessorMask; -} - -#endif - -Status os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData) -{ - const uintptr_t previousAffinity = os_cpu_SetThreadAffinityMask(os_cpu_ProcessorMask()); - - for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++) - { - const uintptr_t processorMask = uintptr_t(1) << processor; - os_cpu_SetThreadAffinityMask(processorMask); - cb(processor, cbData); - } - - (void)os_cpu_SetThreadAffinityMask(previousAffinity); - - return INFO::OK; -} diff --git a/source/lib/sysdep/os/osx/ocpu.cpp b/source/lib/sysdep/os/osx/ocpu.cpp index dabcf707f8..0a68b3a08a 100644 --- a/source/lib/sysdep/os/osx/ocpu.cpp +++ b/source/lib/sysdep/os/osx/ocpu.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 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 @@ -98,24 +98,3 @@ size_t os_cpu_MemoryAvailable() memoryAvailable /= MiB; return memoryAvailable; } - - -uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t /*processorMask*/) -{ - // not yet implemented. when doing so, see http://developer.apple.com/releasenotes/Performance/RN-AffinityAPI/ - - return os_cpu_ProcessorMask(); -} - - -Status os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData) -{ - for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++) - { - const uintptr_t processorMask = uintptr_t(1) << processor; - os_cpu_SetThreadAffinityMask(processorMask); - cb(processor, cbData); - } - - return INFO::OK; -} diff --git a/source/lib/sysdep/os/win/wcpu.cpp b/source/lib/sysdep/os/win/wcpu.cpp index 5f1f83c946..09903142cf 100644 --- a/source/lib/sysdep/os/win/wcpu.cpp +++ b/source/lib/sysdep/os/win/wcpu.cpp @@ -25,7 +25,6 @@ */ #include "precompiled.h" -#include "lib/sysdep/os/win/wcpu.h" #include "lib/sysdep/os_cpu.h" #include "lib/bits.h" @@ -139,100 +138,3 @@ size_t os_cpu_MemoryAvailable() const size_t memoryAvailableMiB = size_t(mse.ullAvailPhys / MiB); return memoryAvailableMiB; } - - -//----------------------------------------------------------------------------- - -DWORD_PTR wcpu_AffinityFromProcessorMask(DWORD_PTR processAffinity, uintptr_t processorMask) -{ - DWORD_PTR affinity = 0; - - size_t processor = (size_t)-1; - for(DWORD processorNumber = 0; processorNumber < (DWORD)os_cpu_MaxProcessors; processorNumber++) - { - if(IsBitSet(processAffinity, processorNumber)) - { - ++processor; // index among the affinity's set bits - - if(IsBitSet(processorMask, processor)) - affinity |= DWORD_PTR(1) << processorNumber; - } - } - - return affinity; -} - -uintptr_t wcpu_ProcessorMaskFromAffinity(DWORD_PTR processAffinity, DWORD_PTR affinity) -{ - uintptr_t processorMask = 0; - - size_t processor = (size_t)-1; - for(DWORD processorNumber = 0; processorNumber < (DWORD)os_cpu_MaxProcessors; processorNumber++) - { - if(IsBitSet(processAffinity, processorNumber)) - { - ++processor; // now corresponds to processorNumber - - if(IsBitSet(affinity, processorNumber)) - processorMask |= uintptr_t(1) << processor; - } - } - - return processorMask; -} - - -//----------------------------------------------------------------------------- - -static void VerifyRunningOnCorrectProcessors(DWORD_PTR affinity) -{ - const DWORD currentProcessor{GetCurrentProcessorNumber()}; - ENSURE(IsBitSet(affinity, currentProcessor)); -} - - -uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask) -{ - const size_t numProcessors = os_cpu_NumProcessors(); - // (avoid undefined result when right shift count >= number of bits) - ENSURE(numProcessors == sizeof(processorMask)*CHAR_BIT || (processorMask >> numProcessors) == 0); - - DWORD_PTR processAffinity, systemAffinity; - const BOOL ok = GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity); - WARN_IF_FALSE(ok); - - const DWORD_PTR affinity = wcpu_AffinityFromProcessorMask(processAffinity, processorMask); - const DWORD_PTR previousAffinity = SetThreadAffinityMask(GetCurrentThread(), affinity); - ENSURE(previousAffinity != 0); // ensure function didn't fail - // (MSDN says SetThreadAffinityMask takes care of rescheduling) - VerifyRunningOnCorrectProcessors(affinity); - - const uintptr_t previousProcessorMask = wcpu_ProcessorMaskFromAffinity(processAffinity, previousAffinity); - return previousProcessorMask; -} - - -Status os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData) -{ - // abort if we can't run on all system processors - DWORD_PTR processAffinity, systemAffinity; - { - const BOOL ok = GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity); - WARN_IF_FALSE(ok); - if(processAffinity != systemAffinity) - return ERR::OS_CPU_RESTRICTED_AFFINITY; // NOWARN - } - - const uintptr_t previousAffinity = os_cpu_SetThreadAffinityMask(os_cpu_ProcessorMask()); - - for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++) - { - const uintptr_t processorMask = uintptr_t(1) << processor; - os_cpu_SetThreadAffinityMask(processorMask); - cb(processor, cbData); - } - - (void)os_cpu_SetThreadAffinityMask(previousAffinity); - - return INFO::OK; -} diff --git a/source/lib/sysdep/os/win/wcpu.h b/source/lib/sysdep/os/win/wcpu.h deleted file mode 100644 index 33569e4c1c..0000000000 --- a/source/lib/sysdep/os/win/wcpu.h +++ /dev/null @@ -1,41 +0,0 @@ -/* 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 - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Windows backend of os_cpu - */ - -#ifndef INCLUDED_WCPU -#define INCLUDED_WCPU - -#include "lib/sysdep/os/win/win.h" - -// "affinity" and "processorNumber" are what Windows sees. -// "processorMask" and "processor" are the idealized representation we expose -// to users. the latter insulates them from process affinity restrictions by -// defining IDs as indices of the nonzero bits within the process affinity. -// these routines are provided for the benefit of wnuma. - -extern DWORD_PTR wcpu_AffinityFromProcessorMask(DWORD_PTR processAffinity, uintptr_t processorMask); -extern uintptr_t wcpu_ProcessorMaskFromAffinity(DWORD_PTR processAffinity, DWORD_PTR affinity); - -#endif // #ifndef INCLUDED_WCPU diff --git a/source/lib/sysdep/os_cpu.cpp b/source/lib/sysdep/os_cpu.cpp index 238c05aabc..7ad1ca7fe0 100644 --- a/source/lib/sysdep/os_cpu.cpp +++ b/source/lib/sysdep/os_cpu.cpp @@ -32,12 +32,6 @@ #include "lib/code_annotation.h" #include "lib/sysdep/os.h" -static const StatusDefinition osCpuStatusDefinitions[] = { - { ERR::OS_CPU_RESTRICTED_AFFINITY, L"Cannot set desired CPU affinity" } -}; -STATUS_ADD_DEFINITIONS(osCpuStatusDefinitions); - - size_t os_cpu_MemorySize() { static size_t memorySize; diff --git a/source/lib/sysdep/os_cpu.h b/source/lib/sysdep/os_cpu.h index 75a082ade7..b5b4f5f8ac 100644 --- a/source/lib/sysdep/os_cpu.h +++ b/source/lib/sysdep/os_cpu.h @@ -33,12 +33,6 @@ #include #include -namespace ERR -{ - const Status OS_CPU_RESTRICTED_AFFINITY = -130100; -} - - //----------------------------------------------------------------------------- // processor topology @@ -102,51 +96,4 @@ size_t os_cpu_MemorySize(); **/ size_t os_cpu_MemoryAvailable(); - -//----------------------------------------------------------------------------- -// scheduling - -/** - * restrict the current thread to a set of processors. - * - * @param processorMask a bit mask of acceptable processors - * (bit index i corresponds to processor i) - * @return the previous mask - **/ -uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask); - -class os_cpu_ScopedSetThreadAffinityMask -{ -public: - os_cpu_ScopedSetThreadAffinityMask(uintptr_t processorMask) - : m_previousProcessorMask(os_cpu_SetThreadAffinityMask(processorMask)) - { - } - - ~os_cpu_ScopedSetThreadAffinityMask() - { - (void)os_cpu_SetThreadAffinityMask(m_previousProcessorMask); - } - -private: - uintptr_t m_previousProcessorMask; -}; - - -/** - * called by os_cpu_CallByEachCPU. - * @param processor ID of processor running the current thread for the - * duration of this function. - * @param cbData user-specified data passed through os_cpu_CallByEachCPU. - **/ -typedef void (*OsCpuCallback)(size_t processor, uintptr_t cbData); - -/** - * execute the specified function once on each processor. - * this proceeds serially (the callback is never reentered) in increasing - * order of processor ID. - * fails if process affinity prevents running on all processors. - **/ -Status os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData); - #endif // #ifndef INCLUDED_OS_CPU