03c059850eaf8183e6a192656adf0df0080d3af5
[xonotic/darkplaces.git] / sys_shared.c
1 #ifdef WIN32
2 # ifndef DONT_USE_SETDLLDIRECTORY
3 #  define _WIN32_WINNT 0x0502
4 # endif
5 #endif
6
7 #include "quakedef.h"
8 #include "thread.h"
9
10 #define SUPPORTDLL
11
12 #ifdef WIN32
13 # include <windows.h>
14 # include <mmsystem.h> // timeGetTime
15 # include <time.h> // localtime
16 #ifdef _MSC_VER
17 #pragma comment(lib, "winmm.lib")
18 #endif
19 #else
20 # include <unistd.h>
21 # include <fcntl.h>
22 # include <sys/time.h>
23 # include <time.h>
24 # ifdef SUPPORTDLL
25 #  include <dlfcn.h>
26 # endif
27 #endif
28
29 static char sys_timestring[128];
30 char *Sys_TimeString(const char *timeformat)
31 {
32         time_t mytime = time(NULL);
33 #if _MSC_VER >= 1400
34         struct tm mytm;
35         localtime_s(&mytm, &mytime);
36         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
37 #else
38         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
39 #endif
40         return sys_timestring;
41 }
42
43
44 extern qboolean host_shuttingdown;
45 void Sys_Quit (int returnvalue)
46 {
47         // Unlock mutexes because the quit command may jump directly here, causing a deadlock
48         Cbuf_UnlockThreadMutex();
49         SV_UnlockThreadMutex();
50
51         if (COM_CheckParm("-profilegameonly"))
52                 Sys_AllowProfiling(false);
53         host_shuttingdown = true;
54         Host_Shutdown();
55         exit(returnvalue);
56 }
57
58 #ifdef __cplusplus
59 extern "C"
60 #endif
61 void Sys_AllowProfiling(qboolean enable)
62 {
63 #ifdef __ANDROID__
64 #ifdef USE_PROFILER
65         extern void monstartup(const char *libname);
66         extern void moncleanup(void);
67         if (enable)
68                 monstartup("libmain.so");
69         else
70                 moncleanup();
71 #endif
72 #elif defined(__linux__) || defined(__FreeBSD__)
73         extern int moncontrol(int);
74         moncontrol(enable);
75 #endif
76 }
77
78
79 /*
80 ===============================================================================
81
82 DLL MANAGEMENT
83
84 ===============================================================================
85 */
86
87 static qboolean Sys_LoadLibraryFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qboolean complain, qboolean has_next)
88 {
89         const dllfunction_t *func;
90         if(dllhandle)
91         {
92                 for (func = fcts; func && func->name != NULL; func++)
93                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
94                         {
95                                 if(complain)
96                                 {
97                                         Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
98                                         if(has_next)
99                                                 Con_DPrintf("\nContinuing with");
100                                 }
101                                 goto notfound;
102                         }
103                 return true;
104
105         notfound:
106                 for (func = fcts; func && func->name != NULL; func++)
107                         *func->funcvariable = NULL;
108         }
109         return false;
110 }
111
112 qboolean Sys_LoadLibrary (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
113 {
114 #ifdef SUPPORTDLL
115         const dllfunction_t *func;
116         dllhandle_t dllhandle = 0;
117         unsigned int i;
118
119         if (handle == NULL)
120                 return false;
121
122 #ifndef WIN32
123 #ifdef PREFER_PRELOAD
124         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
125         if(Sys_LoadLibraryFunctions(dllhandle, fcts, false, false))
126         {
127                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
128                 *handle = dllhandle;
129                 return true;
130         }
131         else
132                 Sys_UnloadLibrary(&dllhandle);
133 notfound:
134 #endif
135 #endif
136
137         // Initializations
138         for (func = fcts; func && func->name != NULL; func++)
139                 *func->funcvariable = NULL;
140
141         // Try every possible name
142         Con_DPrintf ("Trying to load library...");
143         for (i = 0; dllnames[i] != NULL; i++)
144         {
145                 Con_DPrintf (" \"%s\"", dllnames[i]);
146 #ifdef WIN32
147 # ifndef DONT_USE_SETDLLDIRECTORY
148 #  ifdef _WIN64
149                 SetDllDirectory("bin64");
150 #  else
151                 SetDllDirectory("bin32");
152 #  endif
153 # endif
154                 dllhandle = LoadLibrary (dllnames[i]);
155                 // no need to unset this - we want ALL dlls to be loaded from there, anyway
156 #else
157                 dllhandle = dlopen (dllnames[i], RTLD_LAZY | RTLD_GLOBAL);
158 #endif
159                 if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(com_argv[0], '/'))))
160                         break;
161                 else
162                         Sys_UnloadLibrary (&dllhandle);
163         }
164
165         // see if the names can be loaded relative to the executable path
166         // (this is for Mac OSX which does not check next to the executable)
167         if (!dllhandle && strrchr(com_argv[0], '/'))
168         {
169                 char path[MAX_OSPATH];
170                 strlcpy(path, com_argv[0], sizeof(path));
171                 strrchr(path, '/')[1] = 0;
172                 for (i = 0; dllnames[i] != NULL; i++)
173                 {
174                         char temp[MAX_OSPATH];
175                         strlcpy(temp, path, sizeof(temp));
176                         strlcat(temp, dllnames[i], sizeof(temp));
177                         Con_DPrintf (" \"%s\"", temp);
178 #ifdef WIN32
179                         dllhandle = LoadLibrary (temp);
180 #else
181                         dllhandle = dlopen (temp, RTLD_LAZY | RTLD_GLOBAL);
182 #endif
183                         if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, dllnames[i+1] != NULL))
184                                 break;
185                         else
186                                 Sys_UnloadLibrary (&dllhandle);
187                 }
188         }
189
190         // No DLL found
191         if (! dllhandle)
192         {
193                 Con_DPrintf(" - failed.\n");
194                 return false;
195         }
196
197         Con_DPrintf(" - loaded.\n");
198
199         *handle = dllhandle;
200         return true;
201 #else
202         return false;
203 #endif
204 }
205
206 void Sys_UnloadLibrary (dllhandle_t* handle)
207 {
208 #ifdef SUPPORTDLL
209         if (handle == NULL || *handle == NULL)
210                 return;
211
212 #ifdef WIN32
213         FreeLibrary (*handle);
214 #else
215         dlclose (*handle);
216 #endif
217
218         *handle = NULL;
219 #endif
220 }
221
222 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
223 {
224 #ifdef SUPPORTDLL
225 #ifdef WIN32
226         return (void *)GetProcAddress (handle, name);
227 #else
228         return (void *)dlsym (handle, name);
229 #endif
230 #else
231         return NULL;
232 #endif
233 }
234
235 #ifdef WIN32
236 # define HAVE_TIMEGETTIME 1
237 # define HAVE_QUERYPERFORMANCECOUNTER 1
238 # define HAVE_Sleep 1
239 #endif
240
241 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
242 # define HAVE_CLOCKGETTIME 1
243 #endif
244
245 #ifndef WIN32
246 // FIXME improve this check, manpage hints to DST_NONE
247 # define HAVE_GETTIMEOFDAY 1
248 #endif
249
250 #ifndef WIN32
251 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
252 // (so much for POSIX...)
253 # ifdef FD_SET
254 #  define HAVE_SELECT 1
255 # endif
256 #endif
257
258 #ifndef WIN32
259 // FIXME improve this check
260 # define HAVE_USLEEP 1
261 #endif
262
263 // this one is referenced elsewhere
264 cvar_t sys_usenoclockbutbenchmark = {CVAR_SAVE, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
265
266 // these are not
267 static cvar_t sys_debugsleep = {0, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
268 static cvar_t sys_usesdlgetticks = {CVAR_SAVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
269 static cvar_t sys_usesdldelay = {CVAR_SAVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
270 #if HAVE_QUERYPERFORMANCECOUNTER
271 static cvar_t sys_usequeryperformancecounter = {CVAR_SAVE, "sys_usequeryperformancecounter", "0", "use windows QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power) for timing rather than timeGetTime function (which has issues on some motherboards)"};
272 #endif
273 #if HAVE_CLOCKGETTIME
274 static cvar_t sys_useclockgettime = {CVAR_SAVE, "sys_useclockgettime", "1", "use POSIX clock_gettime function (not adjusted by NTP on some older Linux kernels) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
275 #endif
276
277 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
278
279 void Sys_Init_Commands (void)
280 {
281         Cvar_RegisterVariable(&sys_debugsleep);
282         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
283 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
284         if(sys_supportsdlgetticks)
285         {
286                 Cvar_RegisterVariable(&sys_usesdlgetticks);
287                 Cvar_RegisterVariable(&sys_usesdldelay);
288         }
289 #endif
290 #if HAVE_QUERYPERFORMANCECOUNTER
291         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
292 #endif
293 #if HAVE_CLOCKGETTIME
294         Cvar_RegisterVariable(&sys_useclockgettime);
295 #endif
296 }
297
298 double Sys_DirtyTime(void)
299 {
300         // first all the OPTIONAL timers
301
302         // benchmark timer (fake clock)
303         if(sys_usenoclockbutbenchmark.integer)
304         {
305                 double old_benchmark_time = benchmark_time;
306                 benchmark_time += 1;
307                 if(benchmark_time == old_benchmark_time)
308                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
309                 return benchmark_time * 0.000001;
310         }
311 #if HAVE_QUERYPERFORMANCECOUNTER
312         if (sys_usequeryperformancecounter.integer)
313         {
314                 // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
315                 // QueryPerformanceCounter
316                 // platform:
317                 // Windows 95/98/ME/NT/2000/XP
318                 // features:
319                 // very accurate (CPU cycles)
320                 // known issues:
321                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
322                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
323                 double timescale;
324                 LARGE_INTEGER PerformanceFreq;
325                 LARGE_INTEGER PerformanceCount;
326
327                 if (QueryPerformanceFrequency (&PerformanceFreq))
328                 {
329                         QueryPerformanceCounter (&PerformanceCount);
330         
331                         #ifdef __BORLANDC__
332                         timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
333                         return ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
334                         #else
335                         timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
336                         return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
337                         #endif
338                 }
339                 else
340                 {
341                         Con_Printf("No hardware timer available\n");
342                         // fall back to other clock sources
343                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
344                 }
345         }
346 #endif
347
348 #if HAVE_CLOCKGETTIME
349         if (sys_useclockgettime.integer)
350         {
351                 struct timespec ts;
352 #  ifdef CLOCK_MONOTONIC
353                 // linux
354                 clock_gettime(CLOCK_MONOTONIC, &ts);
355 #  else
356                 // sunos
357                 clock_gettime(CLOCK_HIGHRES, &ts);
358 #  endif
359                 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
360         }
361 #endif
362
363         // now all the FALLBACK timers
364         if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
365                 return (double) Sys_SDL_GetTicks() / 1000.0;
366 #if HAVE_GETTIMEOFDAY
367         {
368                 struct timeval tp;
369                 gettimeofday(&tp, NULL);
370                 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
371         }
372 #elif HAVE_TIMEGETTIME
373         {
374                 static int firsttimegettime = true;
375                 // timeGetTime
376                 // platform:
377                 // Windows 95/98/ME/NT/2000/XP
378                 // features:
379                 // reasonable accuracy (millisecond)
380                 // issues:
381                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
382
383                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
384                 if (firsttimegettime)
385                 {
386                         timeBeginPeriod(1);
387                         firsttimegettime = false;
388                 }
389
390                 return (double) timeGetTime() / 1000.0;
391         }
392 #else
393         // fallback for using the SDL timer if no other timer is available
394         // this calls Sys_Error() if not linking against SDL
395         return (double) Sys_SDL_GetTicks() / 1000.0;
396 #endif
397 }
398
399 void Sys_Sleep(int microseconds)
400 {
401         double t = 0;
402         if(sys_usenoclockbutbenchmark.integer)
403         {
404                 if(microseconds)
405                 {
406                         double old_benchmark_time = benchmark_time;
407                         benchmark_time += microseconds;
408                         if(benchmark_time == old_benchmark_time)
409                                 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
410                 }
411                 return;
412         }
413         if(sys_debugsleep.integer)
414         {
415                 t = Sys_DirtyTime();
416         }
417         if(sys_supportsdlgetticks && sys_usesdldelay.integer)
418         {
419                 Sys_SDL_Delay(microseconds / 1000);
420         }
421 #if HAVE_SELECT
422         else
423         {
424                 struct timeval tv;
425                 tv.tv_sec = microseconds / 1000000;
426                 tv.tv_usec = microseconds % 1000000;
427                 select(0, NULL, NULL, NULL, &tv);
428         }
429 #elif HAVE_USLEEP
430         else
431         {
432                 usleep(microseconds);
433         }
434 #elif HAVE_Sleep
435         else
436         {
437                 Sleep(microseconds / 1000);
438         }
439 #else
440         else
441         {
442                 Sys_SDL_Delay(microseconds / 1000);
443         }
444 #endif
445         if(sys_debugsleep.integer)
446         {
447                 t = Sys_DirtyTime() - t;
448                 Sys_PrintfToTerminal("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
449         }
450 }
451
452 void Sys_PrintfToTerminal(const char *fmt, ...)
453 {
454         va_list argptr;
455         char msg[MAX_INPUTLINE];
456
457         va_start(argptr,fmt);
458         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
459         va_end(argptr);
460
461         Sys_PrintToTerminal(msg);
462 }
463
464 #ifndef WIN32
465 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
466 {
467         const char *p = PATH;
468         const char *q;
469         if(p && name)
470         {
471                 while((q = strchr(p, ':')))
472                 {
473                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
474                         if(FS_SysFileExists(buf))
475                                 return buf;
476                         p = q + 1;
477                 }
478                 if(!q) // none found - try the last item
479                 {
480                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
481                         if(FS_SysFileExists(buf))
482                                 return buf;
483                 }
484         }
485         return name;
486 }
487 #endif
488
489 static const char *Sys_FindExecutableName(void)
490 {
491 #if defined(WIN32)
492         return com_argv[0];
493 #else
494         static char exenamebuf[MAX_OSPATH+1];
495         ssize_t n = -1;
496 #if defined(__FreeBSD__)
497         n = readlink("/proc/curproc/file", exenamebuf, sizeof(exenamebuf)-1);
498 #elif defined(__linux__)
499         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
500 #endif
501         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
502         {
503                 exenamebuf[n] = 0;
504                 return exenamebuf;
505         }
506         if(strchr(com_argv[0], '/'))
507                 return com_argv[0]; // possibly a relative path
508         else
509                 return Sys_FindInPATH(com_argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
510 #endif
511 }
512
513 void Sys_ProvideSelfFD(void)
514 {
515         if(com_selffd != -1)
516                 return;
517         com_selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
518 }
519
520 // for x86 cpus only...  (x64 has SSE2_PRESENT)
521 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
522 // code from SDL, shortened as we can expect CPUID to work
523 static int CPUID_Features(void)
524 {
525         int features = 0;
526 # if defined(__GNUC__) && defined(__i386__)
527         __asm__ (
528 "        movl    %%ebx,%%edi\n"
529 "        xorl    %%eax,%%eax                                           \n"
530 "        incl    %%eax                                                 \n"
531 "        cpuid                       # Get family/model/stepping/features\n"
532 "        movl    %%edx,%0                                              \n"
533 "        movl    %%edi,%%ebx\n"
534         : "=m" (features)
535         :
536         : "%eax", "%ecx", "%edx", "%edi"
537         );
538 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
539         __asm {
540         xor     eax, eax
541         inc     eax
542         cpuid                       ; Get family/model/stepping/features
543         mov     features, edx
544         }
545 # else
546 #  error SSE_POSSIBLE set but no CPUID implementation
547 # endif
548         return features;
549 }
550 #endif
551
552 #ifdef SSE_POSSIBLE
553 qboolean Sys_HaveSSE(void)
554 {
555         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
556         if(COM_CheckParm("-nosse"))
557                 return false;
558 #ifdef SSE_PRESENT
559         return true;
560 #else
561         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
562         if(COM_CheckParm("-forcesse") || COM_CheckParm("-forcesse2"))
563                 return true;
564         if(CPUID_Features() & (1 << 25))
565                 return true;
566         return false;
567 #endif
568 }
569
570 qboolean Sys_HaveSSE2(void)
571 {
572         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
573         if(COM_CheckParm("-nosse") || COM_CheckParm("-nosse2"))
574                 return false;
575 #ifdef SSE2_PRESENT
576         return true;
577 #else
578         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
579         if(COM_CheckParm("-forcesse2"))
580                 return true;
581         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
582                 return true;
583         return false;
584 #endif
585 }
586 #endif
587
588 /// called to set process priority for dedicated servers
589 #if defined(__linux__)
590 #include <sys/resource.h>
591 #include <errno.h>
592 static int nicelevel;
593 static qboolean nicepossible;
594 static qboolean isnice;
595 void Sys_InitProcessNice (void)
596 {
597         struct rlimit lim;
598         nicepossible = false;
599         if(COM_CheckParm("-nonice"))
600                 return;
601         errno = 0;
602         nicelevel = getpriority(PRIO_PROCESS, 0);
603         if(errno)
604         {
605                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
606                 return;
607         }
608         if(getrlimit(RLIMIT_NICE, &lim))
609         {
610                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
611                 return;
612         }
613         if(lim.rlim_cur != RLIM_INFINITY && nicelevel < (int) (20 - lim.rlim_cur))
614         {
615                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
616                 return;
617         }
618         nicepossible = true;
619         isnice = false;
620 }
621 void Sys_MakeProcessNice (void)
622 {
623         if(!nicepossible)
624                 return;
625         if(isnice)
626                 return;
627         Con_DPrintf("Process is becoming 'nice'...\n");
628         if(setpriority(PRIO_PROCESS, 0, 19))
629                 Con_Printf("Failed to raise nice level to %d\n", 19);
630         isnice = true;
631 }
632 void Sys_MakeProcessMean (void)
633 {
634         if(!nicepossible)
635                 return;
636         if(!isnice)
637                 return;
638         Con_DPrintf("Process is becoming 'mean'...\n");
639         if(setpriority(PRIO_PROCESS, 0, nicelevel))
640                 Con_Printf("Failed to lower nice level to %d\n", nicelevel);
641         isnice = false;
642 }
643 #else
644 void Sys_InitProcessNice (void)
645 {
646 }
647 void Sys_MakeProcessNice (void)
648 {
649 }
650 void Sys_MakeProcessMean (void)
651 {
652 }
653 #endif