2 # ifndef DONT_USE_SETDLLDIRECTORY
3 # define _WIN32_WINNT 0x0502
16 # include <mmsystem.h> // timeGetTime
17 # include <time.h> // localtime
19 #pragma comment(lib, "winmm.lib")
23 # include <sys/sysctl.h>
27 # include <sys/time.h>
34 static char sys_timestring[128];
35 char *Sys_TimeString(const char *timeformat)
37 time_t mytime = time(NULL);
40 localtime_s(&mytm, &mytime);
41 strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
43 strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
45 return sys_timestring;
49 void Sys_Quit (int returnvalue)
51 // Unlock mutexes because the quit command may jump directly here, causing a deadlock
52 if ((cmd_local)->cbuf->lock)
53 Cbuf_Unlock((cmd_local)->cbuf);
54 SV_UnlockThreadMutex();
55 TaskQueue_Frame(true);
57 if (Sys_CheckParm("-profilegameonly"))
58 Sys_AllowProfiling(false);
59 host.state = host_shutdown;
67 void Sys_AllowProfiling(qbool enable)
71 extern void monstartup(const char *libname);
72 extern void moncleanup(void);
74 monstartup("libmain.so");
78 #elif (defined(__linux__) && (defined(__GLIBC__) || defined(__GNU_LIBRARY__))) || defined(__FreeBSD__)
79 extern int moncontrol(int);
86 ===============================================================================
90 ===============================================================================
93 static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
95 const dllfunction_t *func;
98 for (func = fcts; func && func->name != NULL; func++)
99 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
103 Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
105 Con_DPrintf("\nContinuing with");
112 for (func = fcts; func && func->name != NULL; func++)
113 *func->funcvariable = NULL;
118 qbool Sys_LoadSelf(dllhandle_t *handle)
120 dllhandle_t dllhandle = 0;
125 dllhandle = LoadLibrary (NULL);
127 dllhandle = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
133 qbool Sys_LoadDependency (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
136 const dllfunction_t *func;
137 dllhandle_t dllhandle = 0;
144 #ifdef PREFER_PRELOAD
145 dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
146 if(Sys_LoadDependencyFunctions(dllhandle, fcts, false, false))
148 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
153 Sys_FreeLibrary(&dllhandle);
159 for (func = fcts; func && func->name != NULL; func++)
160 *func->funcvariable = NULL;
162 // Try every possible name
163 Con_DPrintf ("Trying to load library...");
164 for (i = 0; dllnames[i] != NULL; i++)
166 Con_DPrintf (" \"%s\"", dllnames[i]);
168 # ifndef DONT_USE_SETDLLDIRECTORY
170 SetDllDirectory("bin64");
172 SetDllDirectory("bin32");
176 if(Sys_LoadLibrary(dllnames[i], &dllhandle))
178 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
181 Sys_FreeLibrary (&dllhandle);
185 // see if the names can be loaded relative to the executable path
186 // (this is for Mac OSX which does not check next to the executable)
187 if (!dllhandle && strrchr(sys.argv[0], '/'))
189 char path[MAX_OSPATH];
190 strlcpy(path, sys.argv[0], sizeof(path));
191 strrchr(path, '/')[1] = 0;
192 for (i = 0; dllnames[i] != NULL; i++)
194 char temp[MAX_OSPATH];
195 strlcpy(temp, path, sizeof(temp));
196 strlcat(temp, dllnames[i], sizeof(temp));
197 Con_DPrintf (" \"%s\"", temp);
199 if(Sys_LoadLibrary(temp, &dllhandle))
201 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
204 Sys_FreeLibrary (&dllhandle);
212 Con_DPrintf(" - failed.\n");
216 Con_DPrintf(" - loaded.\n");
217 Con_Printf("Loaded library \"%s\"\n", dllnames[i]);
226 qbool Sys_LoadLibrary(const char *name, dllhandle_t *handle)
228 dllhandle_t dllhandle = 0;
235 dllhandle = LoadLibrary (name);
237 dllhandle = dlopen (name, RTLD_LAZY | RTLD_GLOBAL);
247 void Sys_FreeLibrary (dllhandle_t* handle)
250 if (handle == NULL || *handle == NULL)
254 FreeLibrary (*handle);
263 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
267 return (void *)GetProcAddress (handle, name);
269 return (void *)dlsym (handle, name);
277 # define HAVE_TIMEGETTIME 1
278 # define HAVE_QUERYPERFORMANCECOUNTER 1
279 # define HAVE_Sleep 1
283 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
284 # define HAVE_CLOCKGETTIME 1
286 // FIXME improve this check, manpage hints to DST_NONE
287 # define HAVE_GETTIMEOFDAY 1
291 # define HAVE_SELECT 1
295 // FIXME improve this check
296 # define HAVE_USLEEP 1
299 // these are referenced elsewhere
300 cvar_t sys_usenoclockbutbenchmark = {CF_SHARED, "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."};
301 cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
304 static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
305 static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
306 static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
307 #if HAVE_QUERYPERFORMANCECOUNTER
308 static cvar_t sys_usequeryperformancecounter = {CF_SHARED | CF_ARCHIVE, "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)"};
310 #if HAVE_CLOCKGETTIME
311 static cvar_t sys_useclockgettime = {CF_SHARED | CF_ARCHIVE, "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)"};
314 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
320 Returns the position (1 to argc-1) in the program's argument list
321 where the given parameter apears, or 0 if not present
324 int Sys_CheckParm (const char *parm)
328 for (i=1 ; i<sys.argc ; i++)
331 continue; // NEXTSTEP sometimes clears appkit vars.
332 if (!strcmp (parm,sys.argv[i]))
339 void Sys_Init_Commands (void)
341 Cvar_RegisterVariable(&sys_debugsleep);
342 Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
343 Cvar_RegisterVariable(&sys_libdir);
344 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
345 if(sys_supportsdlgetticks)
347 Cvar_RegisterVariable(&sys_usesdlgetticks);
348 Cvar_RegisterVariable(&sys_usesdldelay);
351 #if HAVE_QUERYPERFORMANCECOUNTER
352 Cvar_RegisterVariable(&sys_usequeryperformancecounter);
354 #if HAVE_CLOCKGETTIME
355 Cvar_RegisterVariable(&sys_useclockgettime);
359 double Sys_DirtyTime(void)
361 // first all the OPTIONAL timers
363 // benchmark timer (fake clock)
364 if(sys_usenoclockbutbenchmark.integer)
366 double old_benchmark_time = benchmark_time;
368 if(benchmark_time == old_benchmark_time)
369 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
370 return benchmark_time * 0.000001;
372 #if HAVE_QUERYPERFORMANCECOUNTER
373 if (sys_usequeryperformancecounter.integer)
375 // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
376 // QueryPerformanceCounter
378 // Windows 95/98/ME/NT/2000/XP
380 // very accurate (CPU cycles)
382 // does not necessarily match realtime too well (tends to get faster and faster in win98)
383 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
385 LARGE_INTEGER PerformanceFreq;
386 LARGE_INTEGER PerformanceCount;
388 if (QueryPerformanceFrequency (&PerformanceFreq))
390 QueryPerformanceCounter (&PerformanceCount);
392 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
393 return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
397 Con_Printf("No hardware timer available\n");
398 // fall back to other clock sources
399 Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
404 #if HAVE_CLOCKGETTIME
405 if (sys_useclockgettime.integer)
408 # ifdef CLOCK_MONOTONIC
410 clock_gettime(CLOCK_MONOTONIC, &ts);
413 clock_gettime(CLOCK_HIGHRES, &ts);
415 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
419 // now all the FALLBACK timers
420 if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
421 return (double) Sys_SDL_GetTicks() / 1000.0;
422 #if HAVE_GETTIMEOFDAY
425 gettimeofday(&tp, NULL);
426 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
428 #elif HAVE_TIMEGETTIME
430 static int firsttimegettime = true;
433 // Windows 95/98/ME/NT/2000/XP
435 // reasonable accuracy (millisecond)
437 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
439 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
440 if (firsttimegettime)
443 firsttimegettime = false;
446 return (double) timeGetTime() / 1000.0;
449 // fallback for using the SDL timer if no other timer is available
450 // this calls Sys_Error() if not linking against SDL
451 return (double) Sys_SDL_GetTicks() / 1000.0;
455 extern cvar_t host_maxwait;
456 double Sys_Sleep(double time)
459 uint32_t microseconds;
461 // convert to microseconds
464 if(host_maxwait.value <= 0)
465 time = min(time, 1000000.0);
467 time = min(time, host_maxwait.value * 1000.0);
469 if (time < 1 || host.restless)
470 return 0; // not sleeping this frame
472 microseconds = time; // post-validation to prevent overflow
474 if(sys_usenoclockbutbenchmark.integer)
476 double old_benchmark_time = benchmark_time;
477 benchmark_time += microseconds;
478 if(benchmark_time == old_benchmark_time)
479 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
483 if(sys_debugsleep.integer)
484 Sys_Printf("sys_debugsleep: requesting %u ", microseconds);
485 dt = Sys_DirtyTime();
487 // less important on newer libcurl so no need to disturb dedicated servers
488 if (cls.state != ca_dedicated && Curl_Select(microseconds))
490 // a transfer is ready or we finished sleeping
492 else if(sys_supportsdlgetticks && sys_usesdldelay.integer)
493 Sys_SDL_Delay(microseconds / 1000);
503 if (cls.state == ca_dedicated && sv_checkforpacketsduringsleep.integer)
505 List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
507 if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
509 if (lastfd < s->inetsocket)
510 lastfd = s->inetsocket;
511 #if defined(WIN32) && !defined(_MSC_VER)
512 FD_SET((int)s->inetsocket, &fdreadset);
514 FD_SET((unsigned int)s->inetsocket, &fdreadset);
519 tv.tv_sec = microseconds / 1000000;
520 tv.tv_usec = microseconds % 1000000;
521 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
522 // (so much for POSIX...)
523 // bones_was_here: but a zeroed fd_set seems to be tolerated (tested on Win 7)
524 select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
528 usleep(microseconds);
531 Sleep(microseconds / 1000);
534 Sys_SDL_Delay(microseconds / 1000);
537 dt = Sys_DirtyTime() - dt;
538 if(sys_debugsleep.integer)
539 Sys_Printf(" got %u oversleep %d\n", (unsigned int)(dt * 1000000), (unsigned int)(dt * 1000000) - microseconds);
540 return (dt < 0 || dt >= 1800) ? 0 : dt;
543 void Sys_Printf(const char *fmt, ...)
546 char msg[MAX_INPUTLINE];
548 va_start(argptr,fmt);
549 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
556 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
558 const char *p = PATH;
562 while((q = strchr(p, ':')))
564 dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
565 if(FS_SysFileExists(buf))
569 if(!q) // none found - try the last item
571 dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
572 if(FS_SysFileExists(buf))
580 static const char *Sys_FindExecutableName(void)
585 static char exenamebuf[MAX_OSPATH+1];
587 #if defined(__FreeBSD__)
588 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
589 size_t exenamebuflen = sizeof(exenamebuf)-1;
590 if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
594 #elif defined(__linux__)
595 n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
597 if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
602 if(strchr(sys.argv[0], '/'))
603 return sys.argv[0]; // possibly a relative path
605 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
609 void Sys_ProvideSelfFD(void)
613 sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
616 // for x86 cpus only... (x64 has SSE2_PRESENT)
617 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
618 // code from SDL, shortened as we can expect CPUID to work
619 static int CPUID_Features(void)
622 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
624 " movl %%ebx,%%edi\n"
625 " xorl %%eax,%%eax \n"
627 " cpuid # Get family/model/stepping/features\n"
629 " movl %%edi,%%ebx\n"
632 : "%eax", "%ecx", "%edx", "%edi"
634 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
638 cpuid ; Get family/model/stepping/features
642 # error SSE_POSSIBLE set but no CPUID implementation
649 qbool Sys_HaveSSE(void)
651 // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
652 if(Sys_CheckParm("-nosse"))
657 // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
658 if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
660 if(CPUID_Features() & (1 << 25))
666 qbool Sys_HaveSSE2(void)
668 // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
669 if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
674 // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
675 if(Sys_CheckParm("-forcesse2"))
677 if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
684 /// called to set process priority for dedicated servers
685 #if defined(__linux__)
686 #include <sys/resource.h>
689 void Sys_InitProcessNice (void)
692 sys.nicepossible = false;
693 if(Sys_CheckParm("-nonice"))
696 sys.nicelevel = getpriority(PRIO_PROCESS, 0);
699 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
702 if(getrlimit(RLIMIT_NICE, &lim))
704 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
707 if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
709 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
712 sys.nicepossible = true;
715 void Sys_MakeProcessNice (void)
717 if(!sys.nicepossible)
721 Con_DPrintf("Process is becoming 'nice'...\n");
722 if(setpriority(PRIO_PROCESS, 0, 19))
723 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
726 void Sys_MakeProcessMean (void)
728 if(!sys.nicepossible)
732 Con_DPrintf("Process is becoming 'mean'...\n");
733 if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
734 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
738 void Sys_InitProcessNice (void)
741 void Sys_MakeProcessNice (void)
744 void Sys_MakeProcessMean (void)