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>
36 static char sys_timestring[128];
37 char *Sys_TimeString(const char *timeformat)
39 time_t mytime = time(NULL);
42 localtime_s(&mytm, &mytime);
43 strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
45 strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
47 return sys_timestring;
51 void Sys_Quit (int returnvalue)
53 // Unlock mutexes because the quit command may jump directly here, causing a deadlock
54 if ((cmd_local)->cbuf->lock)
55 Cbuf_Unlock((cmd_local)->cbuf);
56 SV_UnlockThreadMutex();
57 TaskQueue_Frame(true);
59 if (Sys_CheckParm("-profilegameonly"))
60 Sys_AllowProfiling(false);
61 host.state = host_shutdown;
69 void Sys_AllowProfiling(qbool enable)
73 extern void monstartup(const char *libname);
74 extern void moncleanup(void);
76 monstartup("libmain.so");
80 #elif (defined(__linux__) && (defined(__GLIBC__) || defined(__GNU_LIBRARY__))) || defined(__FreeBSD__)
81 extern int moncontrol(int);
88 ===============================================================================
92 ===============================================================================
95 static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
97 const dllfunction_t *func;
100 for (func = fcts; func && func->name != NULL; func++)
101 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
105 Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
107 Con_DPrintf("\nContinuing with");
114 for (func = fcts; func && func->name != NULL; func++)
115 *func->funcvariable = NULL;
120 qbool Sys_LoadSelf(dllhandle_t *handle)
122 dllhandle_t dllhandle = 0;
127 dllhandle = LoadLibrary (NULL);
129 dllhandle = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
135 qbool Sys_LoadDependency (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
138 const dllfunction_t *func;
139 dllhandle_t dllhandle = 0;
146 #ifdef PREFER_PRELOAD
147 dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
148 if(Sys_LoadDependencyFunctions(dllhandle, fcts, false, false))
150 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
155 Sys_FreeLibrary(&dllhandle);
161 for (func = fcts; func && func->name != NULL; func++)
162 *func->funcvariable = NULL;
164 // Try every possible name
165 Con_DPrintf ("Trying to load library...");
166 for (i = 0; dllnames[i] != NULL; i++)
168 Con_DPrintf (" \"%s\"", dllnames[i]);
170 # ifndef DONT_USE_SETDLLDIRECTORY
172 SetDllDirectory("bin64");
174 SetDllDirectory("bin32");
178 if(Sys_LoadLibrary(dllnames[i], &dllhandle))
180 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
183 Sys_FreeLibrary (&dllhandle);
187 // see if the names can be loaded relative to the executable path
188 // (this is for Mac OSX which does not check next to the executable)
189 if (!dllhandle && strrchr(sys.argv[0], '/'))
191 char path[MAX_OSPATH];
192 strlcpy(path, sys.argv[0], sizeof(path));
193 strrchr(path, '/')[1] = 0;
194 for (i = 0; dllnames[i] != NULL; i++)
196 char temp[MAX_OSPATH];
197 strlcpy(temp, path, sizeof(temp));
198 strlcat(temp, dllnames[i], sizeof(temp));
199 Con_DPrintf (" \"%s\"", temp);
201 if(Sys_LoadLibrary(temp, &dllhandle))
203 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
206 Sys_FreeLibrary (&dllhandle);
214 Con_DPrintf(" - failed.\n");
218 Con_DPrintf(" - loaded.\n");
219 Con_Printf("Loaded library \"%s\"\n", dllnames[i]);
228 qbool Sys_LoadLibrary(const char *name, dllhandle_t *handle)
230 dllhandle_t dllhandle = 0;
237 dllhandle = LoadLibrary (name);
239 dllhandle = dlopen (name, RTLD_LAZY | RTLD_GLOBAL);
249 void Sys_FreeLibrary (dllhandle_t* handle)
252 if (handle == NULL || *handle == NULL)
256 FreeLibrary (*handle);
265 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
269 return (void *)GetProcAddress (handle, name);
271 return (void *)dlsym (handle, name);
279 # define HAVE_TIMEGETTIME 1
280 # define HAVE_QUERYPERFORMANCECOUNTER 1
281 # define HAVE_Sleep 1
285 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
286 # define HAVE_CLOCKGETTIME 1
288 // FIXME improve this check, manpage hints to DST_NONE
289 # define HAVE_GETTIMEOFDAY 1
293 # define HAVE_SELECT 1
297 // FIXME improve this check
298 # define HAVE_USLEEP 1
301 // these are referenced elsewhere
302 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."};
303 cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
306 static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
307 static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
308 static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
309 #if HAVE_QUERYPERFORMANCECOUNTER
310 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)"};
312 #if HAVE_CLOCKGETTIME
313 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)"};
316 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
322 Returns the position (1 to argc-1) in the program's argument list
323 where the given parameter apears, or 0 if not present
326 int Sys_CheckParm (const char *parm)
330 for (i=1 ; i<sys.argc ; i++)
333 continue; // NEXTSTEP sometimes clears appkit vars.
334 if (!strcmp (parm,sys.argv[i]))
341 void Sys_Init_Commands (void)
343 Cvar_RegisterVariable(&sys_debugsleep);
344 Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
345 Cvar_RegisterVariable(&sys_libdir);
346 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
347 if(sys_supportsdlgetticks)
349 Cvar_RegisterVariable(&sys_usesdlgetticks);
350 Cvar_RegisterVariable(&sys_usesdldelay);
353 #if HAVE_QUERYPERFORMANCECOUNTER
354 Cvar_RegisterVariable(&sys_usequeryperformancecounter);
356 #if HAVE_CLOCKGETTIME
357 Cvar_RegisterVariable(&sys_useclockgettime);
361 double Sys_DirtyTime(void)
363 // first all the OPTIONAL timers
365 // benchmark timer (fake clock)
366 if(sys_usenoclockbutbenchmark.integer)
368 double old_benchmark_time = benchmark_time;
370 if(benchmark_time == old_benchmark_time)
371 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
372 return benchmark_time * 0.000001;
374 #if HAVE_QUERYPERFORMANCECOUNTER
375 if (sys_usequeryperformancecounter.integer)
377 // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
378 // QueryPerformanceCounter
380 // Windows 95/98/ME/NT/2000/XP
382 // very accurate (CPU cycles)
384 // does not necessarily match realtime too well (tends to get faster and faster in win98)
385 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
387 LARGE_INTEGER PerformanceFreq;
388 LARGE_INTEGER PerformanceCount;
390 if (QueryPerformanceFrequency (&PerformanceFreq))
392 QueryPerformanceCounter (&PerformanceCount);
394 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
395 return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
399 Con_Printf("No hardware timer available\n");
400 // fall back to other clock sources
401 Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
406 #if HAVE_CLOCKGETTIME
407 if (sys_useclockgettime.integer)
410 # ifdef CLOCK_MONOTONIC
412 clock_gettime(CLOCK_MONOTONIC, &ts);
415 clock_gettime(CLOCK_HIGHRES, &ts);
417 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
421 // now all the FALLBACK timers
422 if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
423 return (double) Sys_SDL_GetTicks() / 1000.0;
424 #if HAVE_GETTIMEOFDAY
427 gettimeofday(&tp, NULL);
428 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
430 #elif HAVE_TIMEGETTIME
432 static int firsttimegettime = true;
435 // Windows 95/98/ME/NT/2000/XP
437 // reasonable accuracy (millisecond)
439 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
441 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
442 if (firsttimegettime)
445 firsttimegettime = false;
448 return (double) timeGetTime() / 1000.0;
451 // fallback for using the SDL timer if no other timer is available
452 // this calls Sys_Error() if not linking against SDL
453 return (double) Sys_SDL_GetTicks() / 1000.0;
457 extern cvar_t host_maxwait;
458 double Sys_Sleep(double time)
461 uint32_t microseconds;
463 // convert to microseconds
466 if(host_maxwait.value <= 0)
467 time = min(time, 1000000.0);
469 time = min(time, host_maxwait.value * 1000.0);
471 if (time < 1 || host.restless)
472 return 0; // not sleeping this frame
474 microseconds = time; // post-validation to prevent overflow
476 if(sys_usenoclockbutbenchmark.integer)
478 double old_benchmark_time = benchmark_time;
479 benchmark_time += microseconds;
480 if(benchmark_time == old_benchmark_time)
481 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
485 if(sys_debugsleep.integer)
486 Sys_Printf("sys_debugsleep: requesting %u ", microseconds);
487 dt = Sys_DirtyTime();
489 // less important on newer libcurl so no need to disturb dedicated servers
490 if (cls.state != ca_dedicated && Curl_Select(microseconds))
492 // a transfer is ready or we finished sleeping
494 else if(sys_supportsdlgetticks && sys_usesdldelay.integer)
495 Sys_SDL_Delay(microseconds / 1000);
505 if (cls.state == ca_dedicated && sv_checkforpacketsduringsleep.integer)
507 List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
509 if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
511 if (lastfd < s->inetsocket)
512 lastfd = s->inetsocket;
513 #if defined(WIN32) && !defined(_MSC_VER)
514 FD_SET((int)s->inetsocket, &fdreadset);
516 FD_SET((unsigned int)s->inetsocket, &fdreadset);
521 tv.tv_sec = microseconds / 1000000;
522 tv.tv_usec = microseconds % 1000000;
523 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
524 // (so much for POSIX...)
525 // bones_was_here: but a zeroed fd_set seems to be tolerated (tested on Win 7)
526 select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
530 usleep(microseconds);
533 Sleep(microseconds / 1000);
536 Sys_SDL_Delay(microseconds / 1000);
539 dt = Sys_DirtyTime() - dt;
540 if(sys_debugsleep.integer)
541 Sys_Printf(" got %u oversleep %d\n", (unsigned int)(dt * 1000000), (unsigned int)(dt * 1000000) - microseconds);
542 return (dt < 0 || dt >= 1800) ? 0 : dt;
545 void Sys_Printf(const char *fmt, ...)
548 char msg[MAX_INPUTLINE];
550 va_start(argptr,fmt);
551 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
558 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
560 const char *p = PATH;
564 while((q = strchr(p, ':')))
566 dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
567 if(FS_SysFileExists(buf))
571 if(!q) // none found - try the last item
573 dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
574 if(FS_SysFileExists(buf))
582 static const char *Sys_FindExecutableName(void)
587 static char exenamebuf[MAX_OSPATH+1];
589 #if defined(__FreeBSD__)
590 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
591 size_t exenamebuflen = sizeof(exenamebuf)-1;
592 if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
596 #elif defined(__linux__)
597 n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
599 if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
604 if(strchr(sys.argv[0], '/'))
605 return sys.argv[0]; // possibly a relative path
607 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
611 void Sys_ProvideSelfFD(void)
615 sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
618 // for x86 cpus only... (x64 has SSE2_PRESENT)
619 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
620 // code from SDL, shortened as we can expect CPUID to work
621 static int CPUID_Features(void)
624 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
626 " movl %%ebx,%%edi\n"
627 " xorl %%eax,%%eax \n"
629 " cpuid # Get family/model/stepping/features\n"
631 " movl %%edi,%%ebx\n"
634 : "%eax", "%ecx", "%edx", "%edi"
636 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
640 cpuid ; Get family/model/stepping/features
644 # error SSE_POSSIBLE set but no CPUID implementation
651 qbool Sys_HaveSSE(void)
653 // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
654 if(Sys_CheckParm("-nosse"))
659 // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
660 if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
662 if(CPUID_Features() & (1 << 25))
668 qbool Sys_HaveSSE2(void)
670 // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
671 if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
676 // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
677 if(Sys_CheckParm("-forcesse2"))
679 if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
686 /// called to set process priority for dedicated servers
687 #if defined(__linux__)
688 #include <sys/resource.h>
691 void Sys_InitProcessNice (void)
694 sys.nicepossible = false;
695 if(Sys_CheckParm("-nonice"))
698 sys.nicelevel = getpriority(PRIO_PROCESS, 0);
701 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
704 if(getrlimit(RLIMIT_NICE, &lim))
706 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
709 if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
711 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
714 sys.nicepossible = true;
717 void Sys_MakeProcessNice (void)
719 if(!sys.nicepossible)
723 Con_DPrintf("Process is becoming 'nice'...\n");
724 if(setpriority(PRIO_PROCESS, 0, 19))
725 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
728 void Sys_MakeProcessMean (void)
730 if(!sys.nicepossible)
734 Con_DPrintf("Process is becoming 'mean'...\n");
735 if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
736 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
740 void Sys_InitProcessNice (void)
743 void Sys_MakeProcessNice (void)
746 void Sys_MakeProcessMean (void)
751 int main (int argc, char **argv)
753 signal(SIGFPE, SIG_IGN);
756 sys.argv = (const char **)argv;
758 // COMMANDLINEOPTION: -noterminal disables console output on stdout
759 if(Sys_CheckParm("-noterminal"))
761 // COMMANDLINEOPTION: -stderr moves console output to stderr
762 else if(Sys_CheckParm("-stderr"))
768 Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
771 fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK);
775 Sys_AllowProfiling(true);