Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
wcpu.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 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 /*
24  * Windows implementation of sysdep/cpu
25  */
26 
27 #include "precompiled.h"
28 #include "lib/sysdep/os/win/wcpu.h"
29 #include "lib/sysdep/os_cpu.h"
30 
31 #include "lib/bits.h"
32 #include "lib/alignment.h"
33 #include "lib/module_init.h"
36 
37 
39 {
40  static uintptr_t processorMask;
41 
42  if(!processorMask)
43  {
44  const HANDLE hProcess = GetCurrentProcess();
45  DWORD_PTR processAffinity, systemAffinity;
46  const BOOL ok = GetProcessAffinityMask(hProcess, &processAffinity, &systemAffinity);
47  ENSURE(ok);
48  ENSURE(processAffinity != 0);
49  processorMask = processAffinity;
50  }
51 
52  return processorMask;
53 }
54 
55 
57 {
58  static size_t numProcessors;
59 
60  if(!numProcessors)
61  {
62  numProcessors = PopulationCount(os_cpu_ProcessorMask());
63 
64  // sanity check
65  SYSTEM_INFO si;
66  GetSystemInfo(&si); // guaranteed to succeed
67  ENSURE(numProcessors <= (size_t)si.dwNumberOfProcessors);
68  ENSURE(numProcessors >= 1);
69  }
70 
71  return numProcessors;
72 }
73 
74 
75 //-----------------------------------------------------------------------------
76 
78 {
79  HKEY hKey;
80  if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
81  return ERR::NOT_SUPPORTED;
82 
83  DWORD size = sizeof(freqMhz);
84  LONG ret = RegQueryValueExW(hKey, L"~MHz", 0, 0, (LPBYTE)&freqMhz, &size);
85 
86  RegCloseKey(hKey);
87 
88  if(ret != ERROR_SUCCESS)
90 
91  return INFO::OK;
92 }
93 
94 
96 {
97  static size_t systemPageSize;
98 
99  if(!systemPageSize)
100  {
101  SYSTEM_INFO si;
102  GetSystemInfo(&si); // guaranteed to succeed
103  systemPageSize = (size_t)si.dwPageSize;
104  }
105 
106  return systemPageSize;
107 }
108 
109 
111 {
112  static size_t largePageSize = ~(size_t)0; // "0" has special significance
113 
114  if(largePageSize == ~(size_t)0)
115  {
116  WUTIL_FUNC(pGetLargePageMinimum, SIZE_T, (void));
117  WUTIL_IMPORT_KERNEL32(GetLargePageMinimum, pGetLargePageMinimum);
118  if(pGetLargePageMinimum)
119  {
120  largePageSize = pGetLargePageMinimum();
121  ENSURE(largePageSize != 0); // IA-32 and AMD64 definitely support large pages
122  ENSURE(largePageSize > os_cpu_PageSize());
123  }
124  // no OS support for large pages
125  else
126  largePageSize = 0;
127  }
128 
129  return largePageSize;
130 }
131 
132 
133 static void GetMemoryStatus(MEMORYSTATUSEX& mse)
134 {
135  // note: we no longer bother dynamically importing GlobalMemoryStatusEx -
136  // it's available on Win2k and above. this function safely handles
137  // systems with > 4 GB of memory.
138  mse.dwLength = sizeof(mse);
139  const BOOL ok = GlobalMemoryStatusEx(&mse);
140  WARN_IF_FALSE(ok);
141 }
142 
144 {
145  MEMORYSTATUSEX mse;
146  GetMemoryStatus(mse);
147  DWORDLONG memorySize = mse.ullTotalPhys;
148 
149  // Richter, "Programming Applications for Windows": the reported
150  // value doesn't include non-paged pool reserved during boot;
151  // it's not considered available to the kernel. (the amount is
152  // 528 KiB on a 512 MiB WinXP/Win2k machine). we'll round up
153  // to the nearest megabyte to fix this.
154  memorySize = round_up(memorySize, DWORDLONG(1*MiB)); // (Align<> cannot compute DWORDLONG)
155 
156  return size_t(memorySize / MiB);
157 }
158 
160 {
161  MEMORYSTATUSEX mse;
162  GetMemoryStatus(mse);
163  const size_t memoryAvailableMiB = size_t(mse.ullAvailPhys / MiB);
164  return memoryAvailableMiB;
165 }
166 
167 
168 //-----------------------------------------------------------------------------
169 
170 DWORD_PTR wcpu_AffinityFromProcessorMask(DWORD_PTR processAffinity, uintptr_t processorMask)
171 {
172  DWORD_PTR affinity = 0;
173 
174  size_t processor = (size_t)-1;
175  for(DWORD processorNumber = 0; processorNumber < (DWORD)os_cpu_MaxProcessors; processorNumber++)
176  {
177  if(IsBitSet(processAffinity, processorNumber))
178  {
179  ++processor; // index among the affinity's set bits
180 
181  if(IsBitSet(processorMask, processor))
182  affinity |= DWORD_PTR(1) << processorNumber;
183  }
184  }
185 
186  return affinity;
187 }
188 
189 uintptr_t wcpu_ProcessorMaskFromAffinity(DWORD_PTR processAffinity, DWORD_PTR affinity)
190 {
191  uintptr_t processorMask = 0;
192 
193  size_t processor = (size_t)-1;
194  for(DWORD processorNumber = 0; processorNumber < (DWORD)os_cpu_MaxProcessors; processorNumber++)
195  {
196  if(IsBitSet(processAffinity, processorNumber))
197  {
198  ++processor; // now corresponds to processorNumber
199 
200  if(IsBitSet(affinity, processorNumber))
201  processorMask |= uintptr_t(1) << processor;
202  }
203  }
204 
205  return processorMask;
206 }
207 
208 
209 //-----------------------------------------------------------------------------
210 
211 static void VerifyRunningOnCorrectProcessors(DWORD_PTR affinity)
212 {
213  DWORD currentProcessor;
214 
215  // note: NtGetCurrentProcessorNumber and RtlGetCurrentProcessorNumber aren't
216  // implemented on WinXP SP2.
217  WUTIL_FUNC(pGetCurrentProcessorNumber, DWORD, (void));
218  WUTIL_IMPORT_KERNEL32(GetCurrentProcessorNumber, pGetCurrentProcessorNumber);
219  if(pGetCurrentProcessorNumber)
220  currentProcessor = pGetCurrentProcessorNumber();
221  else
222  {
223  // note: searching for the current APIC ID or IDT address in a
224  // table won't work because initializing the table also requires
225  // this function. LSL only works on Vista (which already
226  // has GetCurrentProcessorNumber).
227  return;
228  }
229 
230  ENSURE(IsBitSet(affinity, currentProcessor));
231 }
232 
233 
234 uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask)
235 {
236  const size_t numProcessors = os_cpu_NumProcessors();
237  // (avoid undefined result when right shift count >= number of bits)
238  ENSURE(numProcessors == sizeof(processorMask)*CHAR_BIT || (processorMask >> numProcessors) == 0);
239 
240  DWORD_PTR processAffinity, systemAffinity;
241  const BOOL ok = GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity);
242  WARN_IF_FALSE(ok);
243 
244  const DWORD_PTR affinity = wcpu_AffinityFromProcessorMask(processAffinity, processorMask);
245  const DWORD_PTR previousAffinity = SetThreadAffinityMask(GetCurrentThread(), affinity);
246  ENSURE(previousAffinity != 0); // ensure function didn't fail
247  // (MSDN says SetThreadAffinityMask takes care of rescheduling)
249 
250  const uintptr_t previousProcessorMask = wcpu_ProcessorMaskFromAffinity(processAffinity, previousAffinity);
251  return previousProcessorMask;
252 }
253 
254 
256 {
257  // abort if we can't run on all system processors
258  DWORD_PTR processAffinity, systemAffinity;
259  {
260  const BOOL ok = GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity);
261  WARN_IF_FALSE(ok);
262  if(processAffinity != systemAffinity)
263  return ERR::OS_CPU_RESTRICTED_AFFINITY; // NOWARN
264  }
265 
266  const uintptr_t previousAffinity = os_cpu_SetThreadAffinityMask(os_cpu_ProcessorMask());
267 
268  for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++)
269  {
270  const uintptr_t processorMask = uintptr_t(1) << processor;
271  os_cpu_SetThreadAffinityMask(processorMask);
272  cb(processor, cbData);
273  }
274 
275  (void)os_cpu_SetThreadAffinityMask(previousAffinity);
276 
277  return INFO::OK;
278 }
size_t os_cpu_QueryMemorySize()
Definition: bcpu.cpp:86
const Status OK
Definition: status.h:386
size_t os_cpu_PageSize()
Definition: bcpu.cpp:68
#define WUTIL_FUNC(varName, ret, params)
Definition: wutil.h:44
static const size_t os_cpu_MaxProcessors
maximum number of processors supported by the OS (determined by the number of bits in an affinity mas...
Definition: os_cpu.h:50
Status os_cpu_CallByEachCPU(OsCpuCallback cb, uintptr_t cbData)
execute the specified function once on each processor.
Definition: bcpu.cpp:115
T round_up(T n, T multiple)
round number up/down to the next given multiple.
Definition: bits.h:265
static size_t PopulationCount(T x)
Definition: bits.h:148
const Status OS_CPU_RESTRICTED_AFFINITY
Definition: os_cpu.h:32
size_t os_cpu_NumProcessors()
Definition: bcpu.cpp:34
size_t os_cpu_LargePageSize()
Definition: bcpu.cpp:79
int BOOL
Definition: wgl.h:51
uintptr_t os_cpu_SetThreadAffinityMask(uintptr_t processorMask)
restrict the current thread to a set of processors.
Definition: bcpu.cpp:109
static HANDLE hProcess
Definition: wdbg_sym.cpp:60
const Status NOT_SUPPORTED
Definition: status.h:429
static void VerifyRunningOnCorrectProcessors(DWORD_PTR affinity)
Definition: wcpu.cpp:211
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
static void GetMemoryStatus(MEMORYSTATUSEX &mse)
Definition: wcpu.cpp:133
void * HANDLE
Definition: wgl.h:62
bool IsBitSet(T value, size_t index)
Definition: bits.h:54
size_t os_cpu_MemoryAvailable()
Definition: bcpu.cpp:98
unsigned long DWORD
Definition: wgl.h:56
uintptr_t wcpu_ProcessorMaskFromAffinity(DWORD_PTR processAffinity, DWORD_PTR affinity)
Definition: wcpu.cpp:189
static const size_t MiB
Definition: alignment.h:72
i64 Status
Error handling system.
Definition: status.h:171
uintptr_t os_cpu_ProcessorMask()
Definition: bcpu.cpp:57
#define u32
Definition: types.h:41
#define WARN_IF_FALSE(expression)
Definition: status.h:360
static const size_t largePageSize
Definition: alignment.h:62
DWORD_PTR wcpu_AffinityFromProcessorMask(DWORD_PTR processAffinity, uintptr_t processorMask)
Definition: wcpu.cpp:170
#define WARN_RETURN(status)
Definition: status.h:255
const Status FAIL
Definition: status.h:406
long LONG
Definition: wgl.h:55
Status wcpu_ReadFrequencyFromRegistry(u32 &freqMhz)
Definition: wcpu.cpp:77
void(* OsCpuCallback)(size_t processor, uintptr_t cbData)
called by os_cpu_CallByEachCPU.
Definition: os_cpu.h:149
#define WUTIL_IMPORT_KERNEL32(procName, varName)
Definition: wutil.h:63