pthread-cond: Fix compilation error on native Windows.
[gnulib.git] / lib / physmem.c
blob5c226b8abf8abdba048dec6f9bb1c3a4a07904d5
1 /* Calculate the size of physical memory.
3 Copyright (C) 2000-2001, 2003, 2005-2006, 2009-2024 Free Software
4 Foundation, Inc.
6 This file is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 This file is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
19 /* Written by Paul Eggert. */
21 #include <config.h>
23 #include "physmem.h"
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <unistd.h>
29 #if HAVE_SYS_PSTAT_H /* HP-UX */
30 # include <sys/pstat.h>
31 #endif
33 #if HAVE_SYS_SYSMP_H /* IRIX */
34 # include <sys/sysmp.h>
35 #endif
37 #if HAVE_SYS_SYSINFO_H
38 /* Linux, AIX, HP-UX, IRIX, OSF/1, Solaris, Cygwin, Android */
39 # include <sys/sysinfo.h>
40 #endif
42 #if HAVE_MACHINE_HAL_SYSINFO_H /* OSF/1 */
43 # include <machine/hal_sysinfo.h>
44 #endif
46 #if HAVE_SYS_TABLE_H /* OSF/1 */
47 # include <sys/table.h>
48 #endif
50 #include <sys/types.h>
52 #if HAVE_SYS_PARAM_H
53 # include <sys/param.h>
54 #endif
56 #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__)
57 /* Linux/musl, macOS, *BSD, IRIX, Minix */
58 # include <sys/sysctl.h>
59 #endif
61 #if HAVE_SYS_SYSTEMCFG_H /* AIX */
62 # include <sys/systemcfg.h>
63 #endif
65 #include "full-read.h"
67 #ifdef _WIN32
69 # define WIN32_LEAN_AND_MEAN
70 # include <windows.h>
72 /* Don't assume that UNICODE is not defined. */
73 # undef GetModuleHandle
74 # define GetModuleHandle GetModuleHandleA
76 /* Avoid warnings from gcc -Wcast-function-type. */
77 # define GetProcAddress \
78 (void *) GetProcAddress
80 /* MEMORYSTATUSEX is missing from older windows headers, so define
81 a local replacement. */
82 typedef struct
84 DWORD dwLength;
85 DWORD dwMemoryLoad;
86 DWORDLONG ullTotalPhys;
87 DWORDLONG ullAvailPhys;
88 DWORDLONG ullTotalPageFile;
89 DWORDLONG ullAvailPageFile;
90 DWORDLONG ullTotalVirtual;
91 DWORDLONG ullAvailVirtual;
92 DWORDLONG ullAvailExtendedVirtual;
93 } lMEMORYSTATUSEX;
94 typedef BOOL (WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*);
96 #endif
98 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
100 /* Return the total amount of physical memory. */
101 double
102 physmem_total (void)
104 #if defined _SC_PHYS_PAGES && defined _SC_PAGESIZE
105 { /* This works on linux-gnu, kfreebsd-gnu, solaris2, and cygwin. */
106 double pages = sysconf (_SC_PHYS_PAGES);
107 double pagesize = sysconf (_SC_PAGESIZE);
108 if (0 <= pages && 0 <= pagesize)
109 return pages * pagesize;
111 #endif
113 #if HAVE_SYSINFO && HAVE_STRUCT_SYSINFO_MEM_UNIT
114 { /* This works on linux. */
115 struct sysinfo si;
116 if (sysinfo(&si) == 0)
117 return (double) si.totalram * si.mem_unit;
119 #endif
121 #if HAVE_PSTAT_GETSTATIC
122 { /* This works on hpux11. */
123 struct pst_static pss;
124 if (0 <= pstat_getstatic (&pss, sizeof pss, 1, 0))
126 double pages = pss.physical_memory;
127 double pagesize = pss.page_size;
128 if (0 <= pages && 0 <= pagesize)
129 return pages * pagesize;
132 #endif
134 #if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE
135 { /* This works on irix6. */
136 struct rminfo realmem;
137 if (sysmp (MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0)
139 double pagesize = sysconf (_SC_PAGESIZE);
140 double pages = realmem.physmem;
141 if (0 <= pages && 0 <= pagesize)
142 return pages * pagesize;
145 #endif
147 #if HAVE_GETSYSINFO && defined GSI_PHYSMEM
148 { /* This works on Tru64 UNIX V4/5. */
149 int physmem;
151 if (getsysinfo (GSI_PHYSMEM, (caddr_t) &physmem, sizeof (physmem),
152 NULL, NULL, NULL) == 1)
154 double kbytes = physmem;
156 if (0 <= kbytes)
157 return kbytes * 1024.0;
160 #endif
162 #if HAVE_SYSCTL && !(defined __GLIBC__ && defined __linux__) && defined HW_PHYSMEM
163 { /* This works on *bsd, kfreebsd-gnu, and darwin. */
164 unsigned int physmem;
165 size_t len = sizeof physmem;
166 static int mib[2] = { CTL_HW, HW_PHYSMEM };
168 if (sysctl (mib, ARRAY_SIZE (mib), &physmem, &len, NULL, 0) == 0
169 && len == sizeof (physmem))
170 return (double) physmem;
172 #endif
174 #if HAVE__SYSTEM_CONFIGURATION
175 /* This works on AIX. */
176 return _system_configuration.physmem;
177 #endif
179 #if defined _WIN32
180 { /* this works on windows */
181 PFN_MS_EX pfnex;
182 HMODULE h = GetModuleHandle ("kernel32.dll");
184 if (!h)
185 return 0.0;
187 /* Use GlobalMemoryStatusEx if available. */
188 if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx")))
190 lMEMORYSTATUSEX lms_ex;
191 lms_ex.dwLength = sizeof lms_ex;
192 if (!pfnex (&lms_ex))
193 return 0.0;
194 return (double) lms_ex.ullTotalPhys;
197 /* Fall back to GlobalMemoryStatus which is always available.
198 but returns wrong results for physical memory > 4GB. */
199 else
201 MEMORYSTATUS ms;
202 GlobalMemoryStatus (&ms);
203 return (double) ms.dwTotalPhys;
206 #endif
208 /* Guess 64 MB. It's probably an older host, so guess small. */
209 return 64 * 1024 * 1024;
212 #if defined __linux__
214 /* Get the amount of free memory and of inactive file cache memory, and
215 return 0. Upon failure, return -1. */
216 static int
217 get_meminfo (unsigned long long *mem_free_p,
218 unsigned long long *mem_inactive_file_p)
220 /* While the sysinfo() system call returns mem_total, mem_free, and a few
221 other numbers, the only way to get mem_inactive_file is by reading
222 /proc/meminfo. */
223 int fd = open ("/proc/meminfo", O_RDONLY);
224 if (fd >= 0)
226 char buf[4096];
227 size_t buf_size = full_read (fd, buf, sizeof (buf));
228 close (fd);
229 if (buf_size > 0)
231 char *buf_end = buf + buf_size;
232 unsigned long long mem_free = 0;
233 unsigned long long mem_inactive_file = 0;
235 /* Iterate through the lines. */
236 char *line = buf;
237 for (;;)
239 char *p;
240 for (p = line; p < buf_end; p++)
241 if (*p == '\n')
242 break;
243 if (p == buf_end)
244 break;
245 *p = '\0';
246 if (sscanf (line, "MemFree: %llu kB", &mem_free) == 1)
248 mem_free *= 1024;
250 if (sscanf (line, "Inactive(file): %llu kB", &mem_inactive_file) == 1)
252 mem_inactive_file *= 1024;
254 line = p + 1;
256 if (mem_free > 0 && mem_inactive_file > 0)
258 *mem_free_p = mem_free;
259 *mem_inactive_file_p = mem_inactive_file;
260 return 0;
264 return -1;
267 #endif
269 /* Return the amount of physical memory that can be claimed, with a given
270 aggressivity. */
271 double
272 physmem_claimable (double aggressivity)
274 #if defined _SC_AVPHYS_PAGES && defined _SC_PAGESIZE
275 # if defined __linux__
276 /* On Linux, sysconf (_SC_AVPHYS_PAGES) returns the amount of "free" memory.
277 The Linux memory management system attempts to keep only a small amount
278 of memory (something like 5% to 10%) as free, because memory is better
279 used in the file cache.
280 We compute the "claimable" memory as
281 (free memory) + aggressivity * (inactive memory in the file cache). */
282 if (aggressivity > 0.0)
284 unsigned long long mem_free;
285 unsigned long long mem_inactive_file;
286 if (get_meminfo (&mem_free, &mem_inactive_file) == 0)
287 return (double) mem_free + aggressivity * (double) mem_inactive_file;
289 # endif
290 { /* This works on linux-gnu, kfreebsd-gnu, solaris2, and cygwin. */
291 double pages = sysconf (_SC_AVPHYS_PAGES);
292 double pagesize = sysconf (_SC_PAGESIZE);
293 if (0 <= pages && 0 <= pagesize)
294 return pages * pagesize;
296 #endif
298 #if HAVE_SYSINFO && HAVE_STRUCT_SYSINFO_MEM_UNIT
299 { /* This works on linux. */
300 struct sysinfo si;
301 if (sysinfo(&si) == 0)
302 return ((double) si.freeram + si.bufferram) * si.mem_unit;
304 #endif
306 #if HAVE_PSTAT_GETSTATIC && HAVE_PSTAT_GETDYNAMIC
307 { /* This works on hpux11. */
308 struct pst_static pss;
309 struct pst_dynamic psd;
310 if (0 <= pstat_getstatic (&pss, sizeof pss, 1, 0)
311 && 0 <= pstat_getdynamic (&psd, sizeof psd, 1, 0))
313 double pages = psd.psd_free;
314 double pagesize = pss.page_size;
315 if (0 <= pages && 0 <= pagesize)
316 return pages * pagesize;
319 #endif
321 #if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE
322 { /* This works on irix6. */
323 struct rminfo realmem;
324 if (sysmp (MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0)
326 double pagesize = sysconf (_SC_PAGESIZE);
327 double pages = realmem.availrmem;
328 if (0 <= pages && 0 <= pagesize)
329 return pages * pagesize;
332 #endif
334 #if HAVE_TABLE && defined TBL_VMSTATS
335 { /* This works on Tru64 UNIX V4/5. */
336 struct tbl_vmstats vmstats;
338 if (table (TBL_VMSTATS, 0, &vmstats, 1, sizeof (vmstats)) == 1)
340 double pages = vmstats.free_count;
341 double pagesize = vmstats.pagesize;
343 if (0 <= pages && 0 <= pagesize)
344 return pages * pagesize;
347 #endif
349 #if HAVE_SYSCTL && !(defined __GLIBC__ && defined __linux__) && defined HW_USERMEM
350 { /* This works on *bsd, kfreebsd-gnu, and darwin. */
351 unsigned int usermem;
352 size_t len = sizeof usermem;
353 static int mib[2] = { CTL_HW, HW_USERMEM };
355 if (sysctl (mib, ARRAY_SIZE (mib), &usermem, &len, NULL, 0) == 0
356 && len == sizeof (usermem))
357 return (double) usermem;
359 #endif
361 #if defined _WIN32
362 { /* this works on windows */
363 PFN_MS_EX pfnex;
364 HMODULE h = GetModuleHandle ("kernel32.dll");
366 if (!h)
367 return 0.0;
369 /* Use GlobalMemoryStatusEx if available. */
370 if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx")))
372 lMEMORYSTATUSEX lms_ex;
373 lms_ex.dwLength = sizeof lms_ex;
374 if (!pfnex (&lms_ex))
375 return 0.0;
376 return (double) lms_ex.ullAvailPhys;
379 /* Fall back to GlobalMemoryStatus which is always available.
380 but returns wrong results for physical memory > 4GB */
381 else
383 MEMORYSTATUS ms;
384 GlobalMemoryStatus (&ms);
385 return (double) ms.dwAvailPhys;
388 #endif
390 /* Guess 25% of physical memory. */
391 return physmem_total () / 4;
394 /* Return the amount of physical memory available. */
395 double
396 physmem_available (void)
398 return physmem_claimable (0.0);
401 #if DEBUG
403 # include <stdio.h>
404 # include <stdlib.h>
407 main (void)
409 printf ("%12.f %12.f\n", physmem_total (), physmem_available ());
410 exit (0);
413 #endif /* DEBUG */
416 Local Variables:
417 compile-command: "gcc -DDEBUG -g -O -Wall -W physmem.c"
418 End: