Skip to content

Commit f040e96

Browse files
ckennellycopybara-github
authored andcommitted
Use possible CPUs to identify NumCPUs() on Linux.
This correctly handles offlined CPUs. PiperOrigin-RevId: 802241608 Change-Id: Idad363516ed3b6027aa2656f3dbc3ee48fc50308
1 parent 7555c7e commit f040e96

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed

absl/base/internal/sysinfo.cc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,69 @@ int Win32NumCPUs() {
135135

136136
#endif
137137

138+
#if defined(__linux__)
139+
// Kinda like perror, but without allocating. At least, mostly...
140+
void PerrorNoAlloc(const char* msg) {
141+
// strerror might allocate, theoretically. In practice, it doesn't do so on
142+
// glibc unless you provide an invalid errno, and never allocates on musl.
143+
char *errmsg = strerror(errno);
144+
145+
// snprintf isn't technically on the async-signal-safe list, but, in practice,
146+
// it doesn't allocate.
147+
char buf[1024];
148+
int len = snprintf(buf, sizeof(buf), "%s: %s\n", msg, errmsg);
149+
if (len > 0) {
150+
if ((unsigned)len > sizeof(buf)) len = sizeof(buf);
151+
ABSL_ATTRIBUTE_UNUSED auto ret = write(2, buf, static_cast<size_t>(len));
152+
}
153+
}
154+
155+
// Returns the number of possible cpus by parsing a sysfs string.
156+
int ParseSysFsPossibleCPUs(const char* str, size_t len) {
157+
if (len == 0) return 1;
158+
// Find the last number in the line and parse that -- that'll be the highest
159+
// cpu number.
160+
for (int i = static_cast<int>(len - 1); i >= 0; --i) {
161+
if (!isdigit(str[i])) return atoi(&str[i+1]) + 1; // NOLINT
162+
}
163+
return atoi(str) + 1; // NOLINT
164+
}
165+
166+
int GetNumPossibleCpusFromSysfs() {
167+
// The "possible" file exists since Linux 2.6.26.
168+
//
169+
// It contains a value such as "0-127" (meaning you have 128 CPUs, numbered 0
170+
// through 127). The format used here also supports strings like: "0,2,4-7" to
171+
// describe discontiguous ids, but that cannot actually happen here, since
172+
// "possible" CPU numbers are always contiguous from 0 to the maximum.
173+
int fd;
174+
do {
175+
fd = open("/sys/devices/system/cpu/possible", O_RDONLY | O_CLOEXEC);
176+
} while (fd < 0 && errno == EINTR);
177+
178+
if (fd < 0) {
179+
PerrorNoAlloc("GetNumPossibleCpusFromSysfs: open() failed");
180+
abort();
181+
}
182+
183+
char buf[1024];
184+
ssize_t len;
185+
do {
186+
len = read(fd, buf, sizeof(buf) - 1);
187+
} while (len < 0 && errno == EINTR);
188+
189+
if (len <= 0) {
190+
PerrorNoAlloc("GetNumPossibleCpusFromSysfs: read() failed");
191+
abort();
192+
}
193+
close(fd);
194+
195+
if (buf[len - 1] == '\n') --len;
196+
buf[len] = '\0';
197+
return ParseSysFsPossibleCPUs(buf, static_cast<size_t>(len));
198+
}
199+
#endif
200+
138201
} // namespace
139202

140203
static int GetNumCPUs() {
@@ -145,6 +208,8 @@ static int GetNumCPUs() {
145208
return hardware_concurrency ? hardware_concurrency : 1;
146209
#elif defined(_AIX)
147210
return sysconf(_SC_NPROCESSORS_ONLN);
211+
#elif defined(__linux__)
212+
return GetNumPossibleCpusFromSysfs();
148213
#else
149214
// Other possibilities:
150215
// - Read /sys/devices/system/cpu/online and use cpumask_parse()

0 commit comments

Comments
 (0)