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