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