Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
lcpu.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2012 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/module_init.h"
29 
30 #if OS_LINUX
31 #include "valgrind.h"
32 #endif
33 
34 
36 {
37  static size_t numProcessors;
38 
39  if(numProcessors == 0)
40  {
41  // Valgrind reports the number of real CPUs, but only emulates a single CPU.
42  // That causes problems when we expect all those CPUs to be distinct, so
43  // just pretend there's only one CPU
44  if (RUNNING_ON_VALGRIND)
45  numProcessors = 1;
46  else
47  {
48  long res = sysconf(_SC_NPROCESSORS_CONF);
49  ENSURE(res != -1);
50  numProcessors = (size_t)res;
51  }
52  }
53 
54  return numProcessors;
55 }
56 
57 
59 {
60  static uintptr_t processorMask;
61 
62  if(!processorMask)
63  processorMask = bit_mask<uintptr_t>(os_cpu_NumProcessors());
64 
65  return processorMask;
66 }
67 
68 
70 {
71  static size_t pageSize;
72 
73  if(!pageSize)
74  pageSize = (size_t)sysconf(_SC_PAGESIZE);
75 
76  return pageSize;
77 }
78 
79 
81 {
82  // assume they're unsupported.
83  return 0;
84 }
85 
86 
88 {
89  const uint64_t memorySize = (uint64_t)sysconf(_SC_PHYS_PAGES) * os_cpu_PageSize();
90  return size_t(memorySize / MiB);
91 }
92 
93 
95 {
96  const uint64_t memoryAvailableBytes = (uint64_t)sysconf(_SC_AVPHYS_PAGES) * os_cpu_PageSize();
97  const size_t memoryAvailable = size_t(memoryAvailableBytes / MiB);
98  return memoryAvailable;
99 }
100 
101 #if OS_ANDROID
102 // the current Android NDK (r7-crystax-4) doesn't support sched_setaffinity,
103 // so provide a stub implementation instead
104 
105 uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t UNUSED(processorMask))
106 {
107  // not yet implemented
108  return os_cpu_ProcessorMask();
109 }
110 
111 #else
112 
113 // glibc __CPU_SETSIZE=1024 is smaller than required on some Linux (4096),
114 // but the CONFIG_NR_CPUS in a header may not reflect the actual kernel,
115 // so we have to detect the limit at runtime.
116 // (see http://trac.wildfiregames.com/ticket/547 for additional information)
117 static size_t maxCpus;
118 
119 static bool IsMaxCpusSufficient()
120 {
121  const size_t setSize = CPU_ALLOC_SIZE(maxCpus);
122  cpu_set_t* set = CPU_ALLOC(maxCpus);
123  ENSURE(set);
124  const int ret = sched_getaffinity(0, setSize, set);
125  CPU_FREE(set);
126  if(ret == 0)
127  return true;
128  ENSURE(errno == EINVAL);
129  return false;
130 }
131 
132 
134 {
135  // the most I have ever heard of is CONFIG_NR_CPUS=4096,
136  // and even that limit should be enough for years and years.
137  for(maxCpus = 64; maxCpus <= 65536; maxCpus *= 2)
138  {
139  if(IsMaxCpusSufficient())
140  return INFO::OK;
141  }
142  return ERR::FAIL;
143 }
144 
145 
146 uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask)
147 {
148  static ModuleInitState maxCpusInitState;
149  (void)ModuleInit(&maxCpusInitState, DetectMaxCpus);
150  const size_t setSize = CPU_ALLOC_SIZE(maxCpus);
151  cpu_set_t* set = CPU_ALLOC(maxCpus);
152  ENSURE(set);
153 
154  uintptr_t previousProcessorMask = 0;
155  {
156  int ret = sched_getaffinity(0, setSize, set);
157  ENSURE(ret == 0);
158 
159  for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++)
160  {
161  if(CPU_ISSET_S(processor, setSize, set))
162  previousProcessorMask |= uintptr_t(1) << processor;
163  }
164  }
165 
166  CPU_ZERO_S(setSize, set);
167  for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++)
168  {
169  if(IsBitSet(processorMask, processor))
170  CPU_SET_S(processor, setSize, set);
171  }
172 
173  int ret = sched_setaffinity(0, setSize, set);
174  ENSURE(ret == 0);
175  // (The process gets migrated immediately by the setaffinity call)
176 
177  CPU_FREE(set);
178  return previousProcessorMask;
179 }
180 
181 #endif
182 
184 {
185  for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++)
186  {
187  const uintptr_t processorMask = uintptr_t(1) << processor;
188  os_cpu_SetThreadAffinityMask(processorMask);
189  cb(processor, cbData);
190  }
191 
192  return INFO::OK;
193 }
size_t os_cpu_QueryMemorySize()
Definition: bcpu.cpp:86
static const size_t pageSize
Definition: alignment.h:61
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
static bool IsMaxCpusSufficient()
Definition: lcpu.cpp:119
const Status OK
Definition: status.h:386
size_t os_cpu_PageSize()
Definition: bcpu.cpp:68
Status os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData)
execute the specified function once on each processor.
Definition: bcpu.cpp:115
size_t os_cpu_NumProcessors()
Definition: bcpu.cpp:34
size_t os_cpu_LargePageSize()
Definition: bcpu.cpp:79
uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask)
restrict the current thread to a set of processors.
Definition: bcpu.cpp:109
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
unsigned long long uint64_t
Definition: wposix_types.h:57
intptr_t ModuleInitState
initialization state of a module (class, source file, etc.) must be initialized to zero (e...
Definition: module_init.h:35
static Status DetectMaxCpus()
Definition: lcpu.cpp:133
bool IsBitSet(T value, size_t index)
Definition: bits.h:54
size_t os_cpu_MemoryAvailable()
Definition: bcpu.cpp:98
static const size_t MiB
Definition: alignment.h:72
static size_t maxCpus
Definition: lcpu.cpp:117
i64 Status
Error handling system.
Definition: status.h:171
uintptr_t os_cpu_ProcessorMask()
Definition: bcpu.cpp:57
const Status FAIL
Definition: status.h:406
void(* OsCpuCallback)(size_t processor, uintptr_t cbData)
called by os_cpu_CallByEachCPU.
Definition: os_cpu.h:149
Status ModuleInit(volatile ModuleInitState *initState, Status(*init)())
calls a user-defined init function if initState is zero.
Definition: module_init.cpp:40