annotate system/System.cpp @ 1779:85903b0e9b42

Update macOS memory size check - the original sysctl was returning far too small a value for modern machines (because bounded to a 32-bit int)
author Chris Cannam
date Wed, 11 Sep 2019 13:20:40 +0100
parents 70e172e6cc59
children c5ee0746bdef
rev   line source
Chris@223 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@223 2
Chris@223 3 /*
Chris@1356 4 Sonic Visualiser
Chris@1356 5 An audio file viewer and annotation editor.
Chris@1356 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1475 7 This file copyright 2006-2018 Chris Cannam and QMUL.
Chris@223 8
Chris@1356 9 This program is free software; you can redistribute it and/or
Chris@1356 10 modify it under the terms of the GNU General Public License as
Chris@1356 11 published by the Free Software Foundation; either version 2 of the
Chris@1356 12 License, or (at your option) any later version. See the file
Chris@1356 13 COPYING included with this distribution for more information.
Chris@223 14 */
Chris@223 15
Chris@223 16 #include "System.h"
Chris@223 17
Chris@223 18 #include <QStringList>
Chris@223 19 #include <QString>
Chris@223 20
Chris@223 21 #include <stdint.h>
Chris@223 22
Chris@223 23 #ifndef _WIN32
Chris@223 24 #include <signal.h>
Chris@223 25 #include <sys/statvfs.h>
Chris@608 26 #include <locale.h>
Chris@608 27 #include <unistd.h>
Chris@223 28 #endif
Chris@223 29
Chris@223 30 #ifdef __APPLE__
Chris@223 31 #include <sys/param.h>
Chris@223 32 #include <sys/sysctl.h>
Chris@223 33 #endif
Chris@223 34
Chris@405 35 #include <limits.h>
Chris@405 36 #include <cstdlib>
Chris@405 37
Chris@223 38 #include <iostream>
Chris@223 39
Chris@255 40 #ifdef __APPLE__
Chris@255 41 extern "C" {
Chris@1356 42 void *
Chris@1356 43 rpl_realloc (void *p, size_t n)
Chris@255 44 {
Chris@1356 45 p = realloc(p, n);
Chris@1356 46 if (p == 0 && n == 0)
Chris@1356 47 {
Chris@1356 48 p = malloc(0);
Chris@1356 49 }
Chris@1356 50 return p;
Chris@255 51 }
Chris@255 52 }
Chris@255 53 #endif
Chris@255 54
Chris@223 55 #ifdef _WIN32
Chris@223 56
Chris@223 57 extern "C" {
Chris@223 58
Chris@1218 59 #ifdef _MSC_VER
Chris@1356 60 void usleep(unsigned long usec)
Chris@1356 61 {
Chris@1356 62 ::Sleep(usec / 1000);
Chris@1356 63 }
Chris@1218 64 #endif
Chris@223 65
Chris@1475 66 int gettimeofday(struct timeval *tv, void * /* tz */)
Chris@1356 67 {
Chris@1356 68 union {
Chris@1356 69 long long ns100;
Chris@1356 70 FILETIME ft;
Chris@1356 71 } now;
Chris@223 72
Chris@1356 73 ::GetSystemTimeAsFileTime(&now.ft);
Chris@1356 74 tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL);
Chris@1356 75 tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL);
Chris@1356 76 return 0;
Chris@1356 77 }
Chris@223 78
Chris@223 79 }
Chris@223 80
Chris@223 81 #endif
Chris@223 82
Chris@223 83 ProcessStatus
Chris@223 84 GetProcessStatus(int pid)
Chris@223 85 {
Chris@223 86 #ifdef _WIN32
Chris@223 87 HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
Chris@223 88 if (!handle) {
Chris@223 89 return ProcessNotRunning;
Chris@223 90 } else {
Chris@223 91 CloseHandle(handle);
Chris@223 92 return ProcessRunning;
Chris@223 93 }
Chris@223 94 #else
Chris@223 95 if (kill(getpid(), 0) == 0) {
Chris@223 96 if (kill(pid, 0) == 0) {
Chris@223 97 return ProcessRunning;
Chris@223 98 } else {
Chris@223 99 return ProcessNotRunning;
Chris@223 100 }
Chris@223 101 } else {
Chris@223 102 return UnknownProcessStatus;
Chris@223 103 }
Chris@223 104 #endif
Chris@223 105 }
Chris@223 106
Chris@223 107 #ifdef _WIN32
Chris@223 108 /* MEMORYSTATUSEX is missing from older Windows headers, so define a
Chris@223 109 local replacement. This trick from MinGW source code. Ugh */
Chris@223 110 typedef struct
Chris@223 111 {
Chris@223 112 DWORD dwLength;
Chris@223 113 DWORD dwMemoryLoad;
Chris@223 114 DWORDLONG ullTotalPhys;
Chris@223 115 DWORDLONG ullAvailPhys;
Chris@223 116 DWORDLONG ullTotalPageFile;
Chris@223 117 DWORDLONG ullAvailPageFile;
Chris@223 118 DWORDLONG ullTotalVirtual;
Chris@223 119 DWORDLONG ullAvailVirtual;
Chris@223 120 DWORDLONG ullAvailExtendedVirtual;
Chris@223 121 } lMEMORYSTATUSEX;
Chris@1218 122 typedef BOOL (WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*);
Chris@223 123 #endif
Chris@223 124
Chris@223 125 void
Chris@1038 126 GetRealMemoryMBAvailable(ssize_t &available, ssize_t &total)
Chris@223 127 {
Chris@223 128 available = -1;
Chris@223 129 total = -1;
Chris@223 130
Chris@223 131 #ifdef _WIN32
Chris@223 132
Chris@223 133 static bool checked = false;
Chris@223 134 static bool exFound = false;
Chris@223 135 static PFN_MS_EX ex;
Chris@223 136
Chris@223 137 if (!checked) {
Chris@223 138
Chris@223 139 HMODULE h = GetModuleHandleA("kernel32.dll");
Chris@223 140
Chris@223 141 if (h) {
Chris@223 142 if ((ex = (PFN_MS_EX)GetProcAddress(h, "GlobalMemoryStatusEx"))) {
Chris@223 143 exFound = true;
Chris@223 144 }
Chris@223 145 }
Chris@223 146
Chris@223 147 checked = true;
Chris@223 148 }
Chris@223 149
Chris@223 150 DWORDLONG wavail = 0;
Chris@223 151 DWORDLONG wtotal = 0;
Chris@223 152
Chris@223 153 if (exFound) {
Chris@223 154
Chris@223 155 lMEMORYSTATUSEX lms;
Chris@1356 156 lms.dwLength = sizeof(lms);
Chris@1356 157 if (!ex(&lms)) {
Chris@843 158 cerr << "WARNING: GlobalMemoryStatusEx failed: error code "
Chris@1356 159 << GetLastError() << endl;
Chris@223 160 return;
Chris@223 161 }
Chris@223 162 wavail = lms.ullAvailPhys;
Chris@223 163 wtotal = lms.ullTotalPhys;
Chris@223 164
Chris@223 165 } else {
Chris@223 166
Chris@223 167 /* Fall back to GlobalMemoryStatus which is always available.
Chris@223 168 but returns wrong results for physical memory > 4GB */
Chris@223 169
Chris@1356 170 MEMORYSTATUS ms;
Chris@1356 171 GlobalMemoryStatus(&ms);
Chris@1356 172 wavail = ms.dwAvailPhys;
Chris@223 173 wtotal = ms.dwTotalPhys;
Chris@223 174 }
Chris@223 175
Chris@223 176 DWORDLONG size = wavail / 1048576;
Chris@223 177 if (size > INT_MAX) size = INT_MAX;
Chris@1038 178 available = ssize_t(size);
Chris@223 179
Chris@223 180 size = wtotal / 1048576;
Chris@223 181 if (size > INT_MAX) size = INT_MAX;
Chris@1038 182 total = ssize_t(size);
Chris@223 183
Chris@223 184 return;
Chris@223 185
Chris@223 186 #else
Chris@223 187 #ifdef __APPLE__
Chris@223 188
Chris@1779 189 unsigned int val32;
Chris@1779 190 int64_t val64;
Chris@223 191 int mib[2];
Chris@223 192 size_t size_sys;
Chris@223 193
Chris@223 194 mib[0] = CTL_HW;
Chris@223 195
Chris@1779 196 mib[1] = HW_MEMSIZE;
Chris@1779 197 size_sys = sizeof(val64);
Chris@1779 198 sysctl(mib, 2, &val64, &size_sys, NULL, 0);
Chris@1779 199 if (val64) total = val64 / 1048576;
Chris@223 200
Chris@223 201 mib[1] = HW_USERMEM;
Chris@1779 202 size_sys = sizeof(val32);
Chris@1779 203 sysctl(mib, 2, &val32, &size_sys, NULL, 0);
Chris@1779 204 if (val32) available = val32 / 1048576;
Chris@1779 205
Chris@1779 206 // The newer memsize sysctl returns a 64-bit value, but usermem is
Chris@1779 207 // an old 32-bit value that doesn't seem to have an updated
Chris@1779 208 // alternative (?) - so it can't return more than 2G. In practice
Chris@1779 209 // it seems to return values far lower than that, even where more
Chris@1779 210 // than 2G of real memory is free. So we can't actually tell when
Chris@1779 211 // we're getting low on memory at all. Most of the time I think we
Chris@1779 212 // just need to use an arbitrary value like this one.
Chris@1779 213 if (available < total/4) {
Chris@1779 214 available = total/4;
Chris@1779 215 }
Chris@223 216
Chris@223 217 return;
Chris@223 218
Chris@223 219 #else
Chris@223 220
Chris@223 221 FILE *meminfo = fopen("/proc/meminfo", "r");
Chris@223 222 if (!meminfo) return;
Chris@223 223
Chris@223 224 char buf[256];
Chris@223 225 while (!feof(meminfo)) {
Chris@1363 226 if (!fgets(buf, 256, meminfo)) {
Chris@1424 227 fclose(meminfo);
Chris@1363 228 return;
Chris@1363 229 }
Chris@223 230 bool isMemFree = (strncmp(buf, "MemFree:", 8) == 0);
Chris@223 231 bool isMemTotal = (!isMemFree && (strncmp(buf, "MemTotal:", 9) == 0));
Chris@223 232 if (isMemFree || isMemTotal) {
Chris@223 233 QString line = QString(buf).trimmed();
Chris@223 234 QStringList elements = line.split(' ', QString::SkipEmptyParts);
Chris@223 235 QString unit = "kB";
Chris@223 236 if (elements.size() > 2) unit = elements[2];
Chris@223 237 int size = elements[1].toInt();
Chris@843 238 // cerr << "have size \"" << size << "\", unit \""
Chris@843 239 // << unit << "\"" << endl;
Chris@223 240 if (unit.toLower() == "gb") size = size * 1024;
Chris@223 241 else if (unit.toLower() == "mb") size = size;
Chris@223 242 else if (unit.toLower() == "kb") size = size / 1024;
Chris@223 243 else size = size / 1048576;
Chris@223 244
Chris@223 245 if (isMemFree) available = size;
Chris@223 246 else total = size;
Chris@223 247 }
Chris@223 248 if (available != -1 && total != -1) {
Chris@223 249 fclose(meminfo);
Chris@223 250 return;
Chris@223 251 }
Chris@223 252 }
Chris@223 253 fclose(meminfo);
Chris@223 254
Chris@223 255 return;
Chris@223 256
Chris@223 257 #endif
Chris@223 258 #endif
Chris@223 259 }
Chris@223 260
Chris@1038 261 ssize_t
Chris@223 262 GetDiscSpaceMBAvailable(const char *path)
Chris@223 263 {
Chris@223 264 #ifdef _WIN32
Chris@223 265 ULARGE_INTEGER available, total, totalFree;
Chris@223 266 if (GetDiskFreeSpaceExA(path, &available, &total, &totalFree)) {
Chris@1356 267 __int64 a = available.QuadPart;
Chris@223 268 a /= 1048576;
Chris@223 269 if (a > INT_MAX) a = INT_MAX;
Chris@1038 270 return ssize_t(a);
Chris@223 271 } else {
Chris@843 272 cerr << "WARNING: GetDiskFreeSpaceEx failed: error code "
Chris@1356 273 << GetLastError() << endl;
Chris@223 274 return -1;
Chris@223 275 }
Chris@223 276 #else
Chris@223 277 struct statvfs buf;
Chris@223 278 if (!statvfs(path, &buf)) {
Chris@223 279 // do the multiplies and divides in this order to reduce the
Chris@223 280 // likelihood of arithmetic overflow
Chris@843 281 // cerr << "statvfs(" << path << ") says available: " << buf.f_bavail << ", block size: " << buf.f_bsize << endl;
Chris@223 282 uint64_t available = ((buf.f_bavail / 1024) * buf.f_bsize) / 1024;
Chris@223 283 if (available > INT_MAX) available = INT_MAX;
Chris@1038 284 return ssize_t(available);
Chris@223 285 } else {
Chris@223 286 perror("statvfs failed");
Chris@223 287 return -1;
Chris@223 288 }
Chris@223 289 #endif
Chris@223 290 }
Chris@303 291
Chris@835 292 #ifdef _WIN32
Chris@835 293 extern void SystemMemoryBarrier()
Chris@835 294 {
Chris@1218 295 #ifdef _MSC_VER
Chris@835 296 MemoryBarrier();
Chris@835 297 #else /* mingw */
Chris@835 298 LONG Barrier = 0;
Chris@835 299 __asm__ __volatile__("xchgl %%eax,%0 "
Chris@835 300 : "=r" (Barrier));
Chris@835 301 #endif
Chris@835 302 }
Chris@835 303 #else /* !_WIN32 */
Chris@1357 304 #if !defined(__APPLE__) && defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ == 0))
Chris@835 305 void
Chris@835 306 SystemMemoryBarrier()
Chris@835 307 {
Chris@835 308 pthread_mutex_t dummy = PTHREAD_MUTEX_INITIALIZER;
Chris@835 309 pthread_mutex_lock(&dummy);
Chris@835 310 pthread_mutex_unlock(&dummy);
Chris@835 311 }
Chris@835 312 #endif /* !defined(__APPLE__) etc */
Chris@835 313 #endif /* !_WIN32 */
Chris@835 314
Chris@835 315
Chris@1582 316 static char *startupLocale = nullptr;
Chris@303 317
Chris@303 318 void
Chris@303 319 StoreStartupLocale()
Chris@303 320 {
Chris@1582 321 char *loc = setlocale(LC_ALL, nullptr);
Chris@303 322 if (!loc) return;
Chris@303 323 if (startupLocale) free(startupLocale);
Chris@303 324 startupLocale = strdup(loc);
Chris@303 325 }
Chris@303 326
Chris@303 327 void
Chris@303 328 RestoreStartupLocale()
Chris@303 329 {
Chris@303 330 if (!startupLocale) {
Chris@303 331 setlocale(LC_ALL, "");
Chris@303 332 } else {
Chris@303 333 setlocale(LC_ALL, startupLocale);
Chris@303 334 }
Chris@303 335 }
Chris@223 336
Chris@223 337 double mod(double x, double y) { return x - (y * floor(x / y)); }
Chris@223 338 float modf(float x, float y) { return x - (y * floorf(x / y)); }
Chris@223 339
Chris@223 340 double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; }
Chris@1039 341 float princargf(float a) { return float(princarg(a)); }
Chris@223 342
Chris@1475 343 bool
Chris@1475 344 getEnvUtf8(std::string variable, std::string &value)
Chris@1475 345 {
Chris@1475 346 value = "";
Chris@1475 347
Chris@1475 348 #ifdef _WIN32
Chris@1475 349 int wvarlen = MultiByteToWideChar(CP_UTF8, 0,
Chris@1475 350 variable.c_str(), int(variable.length()),
Chris@1475 351 0, 0);
Chris@1475 352 if (wvarlen < 0) {
Chris@1475 353 SVCERR << "WARNING: Unable to convert environment variable name "
Chris@1475 354 << variable << " to wide characters" << endl;
Chris@1475 355 return false;
Chris@1475 356 }
Chris@1475 357
Chris@1475 358 wchar_t *wvarbuf = new wchar_t[wvarlen + 1];
Chris@1475 359 (void)MultiByteToWideChar(CP_UTF8, 0,
Chris@1475 360 variable.c_str(), int(variable.length()),
Chris@1475 361 wvarbuf, wvarlen);
Chris@1475 362 wvarbuf[wvarlen] = L'\0';
Chris@1475 363
Chris@1475 364 wchar_t *wvalue = _wgetenv(wvarbuf);
Chris@1176 365
Chris@1475 366 delete[] wvarbuf;
Chris@1475 367
Chris@1475 368 if (!wvalue) {
Chris@1475 369 return false;
Chris@1475 370 }
Chris@1475 371
Chris@1480 372 int wvallen = int(wcslen(wvalue));
Chris@1475 373 int vallen = WideCharToMultiByte(CP_UTF8, 0,
Chris@1480 374 wvalue, wvallen,
Chris@1475 375 0, 0, 0, 0);
Chris@1475 376 if (vallen < 0) {
Chris@1475 377 SVCERR << "WARNING: Unable to convert environment value to UTF-8"
Chris@1475 378 << endl;
Chris@1475 379 return false;
Chris@1475 380 }
Chris@1475 381
Chris@1475 382 char *val = new char[vallen + 1];
Chris@1475 383 (void)WideCharToMultiByte(CP_UTF8, 0,
Chris@1480 384 wvalue, wvallen,
Chris@1475 385 val, vallen, 0, 0);
Chris@1475 386 val[vallen] = '\0';
Chris@1475 387
Chris@1475 388 value = val;
Chris@1475 389
Chris@1475 390 delete[] val;
Chris@1475 391 return true;
Chris@1475 392
Chris@1475 393 #else
Chris@1475 394
Chris@1475 395 char *val = getenv(variable.c_str());
Chris@1475 396 if (!val) {
Chris@1475 397 return false;
Chris@1475 398 }
Chris@1475 399
Chris@1475 400 value = val;
Chris@1475 401 return true;
Chris@1475 402
Chris@1475 403 #endif
Chris@1475 404 }
Chris@1475 405
Chris@1475 406 bool
Chris@1475 407 putEnvUtf8(std::string variable, std::string value)
Chris@1475 408 {
Chris@1477 409 #ifdef _WIN32
Chris@1475 410 std::string entry = variable + "=" + value;
Chris@1475 411
Chris@1475 412 int wentlen = MultiByteToWideChar(CP_UTF8, 0,
Chris@1475 413 entry.c_str(), int(entry.length()),
Chris@1475 414 0, 0);
Chris@1475 415 if (wentlen < 0) {
Chris@1475 416 SVCERR << "WARNING: Unable to convert environment entry to "
Chris@1475 417 << "wide characters" << endl;
Chris@1475 418 return false;
Chris@1475 419 }
Chris@1475 420
Chris@1475 421 wchar_t *wentbuf = new wchar_t[wentlen + 1];
Chris@1475 422 (void)MultiByteToWideChar(CP_UTF8, 0,
Chris@1475 423 entry.c_str(), int(entry.length()),
Chris@1475 424 wentbuf, wentlen);
Chris@1475 425 wentbuf[wentlen] = L'\0';
Chris@1475 426
Chris@1475 427 int rv = _wputenv(wentbuf);
Chris@1475 428
Chris@1475 429 delete[] wentbuf;
Chris@1475 430
Chris@1475 431 if (rv != 0) {
Chris@1475 432 SVCERR << "WARNING: Failed to set environment entry" << endl;
Chris@1475 433 return false;
Chris@1475 434 }
Chris@1475 435 return true;
Chris@1475 436
Chris@1475 437 #else
Chris@1475 438
Chris@1477 439 int rv = setenv(variable.c_str(), value.c_str(), 1);
Chris@1475 440 if (rv != 0) {
Chris@1475 441 SVCERR << "WARNING: Failed to set environment entry" << endl;
Chris@1475 442 return false;
Chris@1475 443 }
Chris@1475 444 return true;
Chris@1475 445
Chris@1475 446 #endif
Chris@1475 447 }
Chris@1475 448