LCOV - code coverage report
Current view: top level - source/lib/sysdep/os/linux - lcpu.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 66 1.5 %
Date: 2023-01-19 00:18:29 Functions: 2 12 16.7 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : #include "precompiled.h"
      24             : 
      25             : #include "lib/sysdep/os_cpu.h"
      26             : #include "lib/alignment.h"
      27             : #include "lib/bits.h"
      28             : #include "lib/config2.h"
      29             : #include "lib/module_init.h"
      30             : 
      31             : #if CONFIG2_VALGRIND
      32             : # include "valgrind.h"
      33             : #endif
      34             : 
      35             : 
      36           0 : size_t os_cpu_NumProcessors()
      37             : {
      38             :     static size_t numProcessors;
      39             : 
      40           0 :     if(numProcessors == 0)
      41             :     {
      42             : #if CONFIG2_VALGRIND
      43             :         // Valgrind reports the number of real CPUs, but only emulates a single CPU.
      44             :         // That causes problems when we expect all those CPUs to be distinct, so
      45             :         // just pretend there's only one CPU
      46             :         if (RUNNING_ON_VALGRIND)
      47             :             numProcessors = 1;
      48             :         else
      49             : #endif
      50             :         {
      51           0 :             long res = sysconf(_SC_NPROCESSORS_CONF);
      52           0 :             ENSURE(res != -1);
      53           0 :             numProcessors = (size_t)res;
      54             :         }
      55             :     }
      56             : 
      57           0 :     return numProcessors;
      58             : }
      59             : 
      60             : 
      61           0 : uintptr_t os_cpu_ProcessorMask()
      62             : {
      63             :     static uintptr_t processorMask;
      64             : 
      65           0 :     if(!processorMask)
      66           0 :         processorMask = bit_mask<uintptr_t>(os_cpu_NumProcessors());
      67             : 
      68           0 :     return processorMask;
      69             : }
      70             : 
      71             : 
      72           0 : size_t os_cpu_PageSize()
      73             : {
      74             :     static size_t pageSize;
      75             : 
      76           0 :     if(!pageSize)
      77           0 :         pageSize = (size_t)sysconf(_SC_PAGESIZE);
      78             : 
      79           0 :     return pageSize;
      80             : }
      81             : 
      82             : 
      83           0 : size_t os_cpu_LargePageSize()
      84             : {
      85             :     // assume they're unsupported.
      86           0 :     return 0;
      87             : }
      88             : 
      89             : 
      90           0 : size_t os_cpu_QueryMemorySize()
      91             : {
      92           0 :     const uint64_t memorySize = (uint64_t)sysconf(_SC_PHYS_PAGES) * os_cpu_PageSize();
      93           0 :     return size_t(memorySize / MiB);
      94             : }
      95             : 
      96             : 
      97           0 : size_t os_cpu_MemoryAvailable()
      98             : {
      99           0 :     const uint64_t memoryAvailableBytes = (uint64_t)sysconf(_SC_AVPHYS_PAGES) * os_cpu_PageSize();
     100           0 :     const size_t memoryAvailable = size_t(memoryAvailableBytes / MiB);
     101           0 :     return memoryAvailable;
     102             : }
     103             : 
     104             : #if OS_ANDROID
     105             : // the current Android NDK (r7-crystax-4) doesn't support sched_setaffinity,
     106             : // so provide a stub implementation instead
     107             : 
     108             : uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t UNUSED(processorMask))
     109             : {
     110             :     // not yet implemented
     111             :     return os_cpu_ProcessorMask();
     112             : }
     113             : 
     114             : #else
     115             : 
     116             : // glibc __CPU_SETSIZE=1024 is smaller than required on some Linux (4096),
     117             : // but the CONFIG_NR_CPUS in a header may not reflect the actual kernel,
     118             : // so we have to detect the limit at runtime.
     119             : // (see http://trac.wildfiregames.com/ticket/547 for additional information)
     120             : static size_t maxCpus;
     121             : 
     122           0 : static bool IsMaxCpusSufficient()
     123             : {
     124           0 :     const size_t setSize = CPU_ALLOC_SIZE(maxCpus);
     125           0 :     cpu_set_t* set = CPU_ALLOC(maxCpus);
     126           0 :     ENSURE(set);
     127           0 :     const int ret = sched_getaffinity(0, setSize, set);
     128           0 :     CPU_FREE(set);
     129           0 :     if(ret == 0)
     130           0 :         return true;
     131           0 :     ENSURE(errno == EINVAL);
     132           0 :     return false;
     133             : }
     134             : 
     135             : 
     136           0 : static Status DetectMaxCpus()
     137             : {
     138             :     // the most I have ever heard of is CONFIG_NR_CPUS=4096,
     139             :     // and even that limit should be enough for years and years.
     140           0 :     for(maxCpus = 64; maxCpus <= 65536; maxCpus *= 2)
     141             :     {
     142           0 :         if(IsMaxCpusSufficient())
     143           0 :             return INFO::OK;
     144             :     }
     145           0 :     return ERR::FAIL;
     146             : }
     147             : 
     148             : 
     149           0 : uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask)
     150             : {
     151             :     static ModuleInitState maxCpusInitState;
     152           0 :     (void)ModuleInit(&maxCpusInitState, DetectMaxCpus);
     153           0 :     const size_t setSize = CPU_ALLOC_SIZE(maxCpus);
     154           0 :     cpu_set_t* set = CPU_ALLOC(maxCpus);
     155           0 :     ENSURE(set);
     156             : 
     157           0 :     uintptr_t previousProcessorMask = 0;
     158             :     {
     159           0 :         int ret = sched_getaffinity(0, setSize, set);
     160           0 :         ENSURE(ret == 0);
     161             : 
     162           0 :         for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++)
     163             :         {
     164           0 :             if(CPU_ISSET_S(processor, setSize, set))
     165           0 :                 previousProcessorMask |= uintptr_t(1) << processor;
     166             :         }
     167             :     }
     168             : 
     169           0 :     CPU_ZERO_S(setSize, set);
     170           0 :     for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++)
     171             :     {
     172           0 :         if(IsBitSet(processorMask, processor))
     173           0 :             CPU_SET_S(processor, setSize, set);
     174             :     }
     175             : 
     176           0 :     int ret = sched_setaffinity(0, setSize, set);
     177           0 :     ENSURE(ret == 0);
     178             :     // (The process gets migrated immediately by the setaffinity call)
     179             : 
     180           0 :     CPU_FREE(set);
     181           0 :     return previousProcessorMask;
     182             : }
     183             : 
     184             : #endif
     185             : 
     186           0 : Status os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData)
     187             : {
     188           0 :     const uintptr_t previousAffinity = os_cpu_SetThreadAffinityMask(os_cpu_ProcessorMask());
     189             : 
     190           0 :     for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++)
     191             :     {
     192           0 :         const uintptr_t processorMask = uintptr_t(1) << processor;
     193           0 :         os_cpu_SetThreadAffinityMask(processorMask);
     194           0 :         cb(processor, cbData);
     195             :     }
     196             : 
     197           0 :     (void)os_cpu_SetThreadAffinityMask(previousAffinity);
     198             : 
     199           0 :     return INFO::OK;
     200           3 : }

Generated by: LCOV version 1.13