]> icculus.org git repositories - divverent/darkplaces.git/blob - sys_shared.c
also: sys_usesdldelay for using SDL_Delay() for sleeping instead of something better...
[divverent/darkplaces.git] / sys_shared.c
1 #include "quakedef.h"
2
3 #define SUPPORTDLL
4
5 #ifndef WIN32
6 # include <unistd.h>
7 # include <fcntl.h>
8 # include <sys/time.h>
9 # include <time.h>
10 #ifdef SUPPORTDLL
11 # include <dlfcn.h>
12 #endif
13 #endif
14
15 static char sys_timestring[128];
16 char *Sys_TimeString(const char *timeformat)
17 {
18         time_t mytime = time(NULL);
19 #if _MSC_VER >= 1400
20         struct tm mytm;
21         localtime_s(&mytm, &mytime);
22         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
23 #else
24         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
25 #endif
26         return sys_timestring;
27 }
28
29
30 extern qboolean host_shuttingdown;
31 void Sys_Quit (int returnvalue)
32 {
33         if (COM_CheckParm("-profilegameonly"))
34                 Sys_AllowProfiling(false);
35         host_shuttingdown = true;
36         Host_Shutdown();
37         exit(returnvalue);
38 }
39
40 #if defined(__linux__) || defined(__FreeBSD__)
41 #ifdef __cplusplus
42 extern "C"
43 #endif
44 int moncontrol(int);
45 #endif
46
47 void Sys_AllowProfiling(qboolean enable)
48 {
49 #if defined(__linux__) || defined(__FreeBSD__)
50         moncontrol(enable);
51 #endif
52 }
53
54
55 /*
56 ===============================================================================
57
58 DLL MANAGEMENT
59
60 ===============================================================================
61 */
62
63 qboolean Sys_LoadLibrary (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
64 {
65 #ifdef SUPPORTDLL
66         const dllfunction_t *func;
67         dllhandle_t dllhandle = 0;
68         unsigned int i;
69
70         if (handle == NULL)
71                 return false;
72
73 #ifndef WIN32
74 #ifdef PREFER_PRELOAD
75         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
76         if(dllhandle)
77         {
78                 for (func = fcts; func && func->name != NULL; func++)
79                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
80                         {
81                                 dlclose(dllhandle);
82                                 goto notfound;
83                         }
84                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
85                 *handle = dllhandle;
86                 return true;
87         }
88 notfound:
89 #endif
90 #endif
91
92         // Initializations
93         for (func = fcts; func && func->name != NULL; func++)
94                 *func->funcvariable = NULL;
95
96         // Try every possible name
97         Con_DPrintf ("Trying to load library...");
98         for (i = 0; dllnames[i] != NULL; i++)
99         {
100                 Con_DPrintf (" \"%s\"", dllnames[i]);
101 #ifdef WIN32
102                 dllhandle = LoadLibrary (dllnames[i]);
103 #else
104                 dllhandle = dlopen (dllnames[i], RTLD_LAZY | RTLD_GLOBAL);
105 #endif
106                 if (dllhandle)
107                         break;
108         }
109
110         // see if the names can be loaded relative to the executable path
111         // (this is for Mac OSX which does not check next to the executable)
112         if (!dllhandle && strrchr(com_argv[0], '/'))
113         {
114                 char path[MAX_OSPATH];
115                 strlcpy(path, com_argv[0], sizeof(path));
116                 strrchr(path, '/')[1] = 0;
117                 for (i = 0; dllnames[i] != NULL; i++)
118                 {
119                         char temp[MAX_OSPATH];
120                         strlcpy(temp, path, sizeof(temp));
121                         strlcat(temp, dllnames[i], sizeof(temp));
122                         Con_DPrintf (" \"%s\"", temp);
123 #ifdef WIN32
124                         dllhandle = LoadLibrary (temp);
125 #else
126                         dllhandle = dlopen (temp, RTLD_LAZY | RTLD_GLOBAL);
127 #endif
128                         if (dllhandle)
129                                 break;
130                 }
131         }
132
133         // No DLL found
134         if (! dllhandle)
135         {
136                 Con_DPrintf(" - failed.\n");
137                 return false;
138         }
139
140         Con_DPrintf(" - loaded.\n");
141
142         // Get the function adresses
143         for (func = fcts; func && func->name != NULL; func++)
144                 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
145                 {
146                         Con_DPrintf ("Missing function \"%s\" - broken library!\n", func->name);
147                         Sys_UnloadLibrary (&dllhandle);
148                         return false;
149                 }
150
151         *handle = dllhandle;
152         return true;
153 #else
154         return false;
155 #endif
156 }
157
158 void Sys_UnloadLibrary (dllhandle_t* handle)
159 {
160 #ifdef SUPPORTDLL
161         if (handle == NULL || *handle == NULL)
162                 return;
163
164 #ifdef WIN32
165         FreeLibrary (*handle);
166 #else
167         dlclose (*handle);
168 #endif
169
170         *handle = NULL;
171 #endif
172 }
173
174 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
175 {
176 #ifdef SUPPORTDLL
177 #ifdef WIN32
178         return (void *)GetProcAddress (handle, name);
179 #else
180         return (void *)dlsym (handle, name);
181 #endif
182 #else
183         return NULL;
184 #endif
185 }
186
187 #ifdef WIN32
188 # define HAVE_TIMEGETTIME 1
189 # define HAVE_QUERYPERFORMANCECOUNTER 1
190 # define HAVE_Sleep 1
191 #endif
192
193 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
194 # define HAVE_CLOCKGETTIME 1
195 #endif
196
197 #ifndef WIN32
198 // FIXME improve this check, manpage hints to DST_NONE
199 # define HAVE_GETTIMEOFDAY 1
200 #endif
201
202 #ifdef FD_SET
203 # define HAVE_SELECT 1
204 #endif
205
206 #ifndef WIN32
207 // FIXME improve this check
208 # define HAVE_USLEEP 1
209 #endif
210
211 cvar_t sys_debugsleep = {0, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
212 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."};
213 cvar_t sys_usesdlgetticks = {CVAR_SAVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
214 cvar_t sys_usesdldelay = {CVAR_SAVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
215 #if HAVE_QUERYPERFORMANCECOUNTER
216 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)"};
217 #endif
218 #if HAVE_CLOCKGETTIME
219 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)"};
220 #endif
221
222 static unsigned long benchmark_time;
223
224 void Sys_Init_Commands (void)
225 {
226         Cvar_RegisterVariable(&sys_debugsleep);
227         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
228 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
229         if(sys_supportsdlgetticks)
230         {
231                 Cvar_RegisterVariable(&sys_usesdlgetticks);
232                 Cvar_RegisterVariable(&sys_usesdldelay);
233         }
234 #endif
235 #if HAVE_QUERYPERFORMANCECOUNTER
236         Cvar_RegisterVariable(&sys_usetimegettime);
237 #endif
238 #if HAVE_CLOCKGETTIME
239         Cvar_RegisterVariable(&sys_useclockgettime);
240 #endif
241 }
242
243 double Sys_DoubleTime(void)
244 {
245         static int first = true;
246         static double oldtime = 0.0, curtime = 0.0;
247         double newtime;
248         if(sys_usenoclockbutbenchmark.integer)
249         {
250                 benchmark_time += 1;
251                 return ((double) benchmark_time) / 1e6;
252         }
253
254         // first all the OPTIONAL timers
255
256 #if HAVE_QUERYPERFORMANCECOUNTER
257         else if (sys_usequeryperformancecounter.integer)
258         {
259                 // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
260                 // QueryPerformanceCounter
261                 // platform:
262                 // Windows 95/98/ME/NT/2000/XP
263                 // features:
264                 // very accurate (CPU cycles)
265                 // known issues:
266                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
267                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
268                 double timescale;
269                 LARGE_INTEGER PerformanceFreq;
270                 LARGE_INTEGER PerformanceCount;
271
272                 if (!QueryPerformanceFrequency (&PerformanceFreq))
273                 {
274                         Con_Printf ("No hardware timer available\n");
275                         // fall back to timeGetTime
276                         Cvar_SetValueQuick(&sys_usetimegettime, true);
277                         return Sys_DoubleTime();
278                 }
279                 QueryPerformanceCounter (&PerformanceCount);
280
281                 #ifdef __BORLANDC__
282                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
283                 newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
284                 #else
285                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
286                 newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
287                 #endif
288         }
289 #endif
290
291 #if HAVE_CLOCKGETTIME
292         else if (sys_useclockgettime.integer)
293         {
294                 struct timespec ts;
295 #  ifdef CLOCK_MONOTONIC
296                 // linux
297                 clock_gettime(CLOCK_MONOTONIC, &ts);
298 #  else
299                 // sunos
300                 clock_gettime(CLOCK_HIGHRES, &ts);
301 #  endif
302                 newtime = (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
303         }
304 #endif
305
306         // now all the FALLBACK timers
307         else if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
308         {
309                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
310         }
311 #if HAVE_GETTIMEOFDAY
312         else
313         {
314                 struct timeval tp;
315                 gettimeofday(&tp, NULL);
316                 newtime = (double) tp.tv_sec + tp.tv_usec / 1000000.0;
317         }
318 #elif HAVE_TIMEGETTIME
319         else
320         {
321                 static int firsttimegettime = true;
322                 // timeGetTime
323                 // platform:
324                 // Windows 95/98/ME/NT/2000/XP
325                 // features:
326                 // reasonable accuracy (millisecond)
327                 // issues:
328                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
329
330                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
331                 if (firsttimegettime)
332                 {
333                         timeBeginPeriod (1);
334                         firsttimegettime = false;
335                 }
336
337                 newtime = (double) timeGetTime () / 1000.0;
338         }
339 #else
340         // fallback for using the SDL timer if no other timer is available
341         else
342         {
343                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
344                 // this calls Sys_Error() if not linking against SDL
345         }
346 #endif
347
348         if (first)
349         {
350                 first = false;
351                 oldtime = newtime;
352         }
353
354         if (newtime < oldtime)
355         {
356                 // warn if it's significant
357                 if (newtime - oldtime < -0.01)
358                         Con_Printf("Sys_DoubleTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
359         }
360         else if (newtime > oldtime + 1800)
361         {
362                 Con_Printf("Sys_DoubleTime: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
363         }
364         else
365                 curtime += newtime - oldtime;
366         oldtime = newtime;
367
368         return curtime;
369 }
370
371 void Sys_Sleep(int microseconds)
372 {
373         double t;
374         if(sys_usenoclockbutbenchmark.integer)
375         {
376                 benchmark_time += microseconds;
377                 return;
378         }
379         if(sys_debugsleep.integer)
380         {
381                 t = Sys_DoubleTime();
382         }
383         if(sys_supportsdlgetticks && sys_usesdldelay.integer)
384         {
385                 Sys_SDL_Delay(microseconds / 1000);
386         }
387 #if HAVE_SELECT
388         else
389         {
390                 struct timeval tv;
391                 tv.tv_sec = microseconds / 1000000;
392                 tv.tv_usec = microseconds % 1000000;
393                 select(0, NULL, NULL, NULL, &tv);
394         }
395 #elif HAVE_USLEEP
396         else
397         {
398                 usleep(microseconds);
399         }
400 #elif HAVE_Sleep
401         else
402         {
403                 Sleep(microseconds / 1000);
404         }
405 #else
406         else
407         {
408                 Sys_SDL_Delay(microseconds / 1000);
409         }
410 #endif
411         if(sys_debugsleep.integer)
412         {
413                 t = Sys_DoubleTime() - t;
414                 printf("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
415         }
416 }