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