SDL_systimer.c (6088B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 #include "../../SDL_internal.h" 22 23 #ifdef SDL_TIMER_UNIX 24 25 #include <stdio.h> 26 #include <sys/time.h> 27 #include <unistd.h> 28 #include <errno.h> 29 30 #include "SDL_timer.h" 31 #include "SDL_hints.h" 32 #include "../SDL_timer_c.h" 33 34 #ifdef __EMSCRIPTEN__ 35 #include <emscripten.h> 36 #endif 37 38 /* The clock_gettime provides monotonous time, so we should use it if 39 it's available. The clock_gettime function is behind ifdef 40 for __USE_POSIX199309 41 Tommi Kyntola (tommi.kyntola@ray.fi) 27/09/2005 42 */ 43 /* Reworked monotonic clock to not assume the current system has one 44 as not all linux kernels provide a monotonic clock (yeah recent ones 45 probably do) 46 Also added OS X Monotonic clock support 47 Based on work in https://github.com/ThomasHabets/monotonic_clock 48 */ 49 #if HAVE_NANOSLEEP || HAVE_CLOCK_GETTIME 50 #include <time.h> 51 #endif 52 #ifdef __APPLE__ 53 #include <mach/mach_time.h> 54 #endif 55 56 /* Use CLOCK_MONOTONIC_RAW, if available, which is not subject to adjustment by NTP */ 57 #if HAVE_CLOCK_GETTIME 58 #ifdef CLOCK_MONOTONIC_RAW 59 #define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC_RAW 60 #else 61 #define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC 62 #endif 63 #endif 64 65 /* The first ticks value of the application */ 66 #if HAVE_CLOCK_GETTIME 67 static struct timespec start_ts; 68 #elif defined(__APPLE__) 69 static uint64_t start_mach; 70 mach_timebase_info_data_t mach_base_info; 71 #endif 72 static SDL_bool has_monotonic_time = SDL_FALSE; 73 static struct timeval start_tv; 74 static SDL_bool ticks_started = SDL_FALSE; 75 76 void 77 SDL_TicksInit(void) 78 { 79 if (ticks_started) { 80 return; 81 } 82 ticks_started = SDL_TRUE; 83 84 /* Set first ticks value */ 85 #if HAVE_CLOCK_GETTIME 86 if (clock_gettime(SDL_MONOTONIC_CLOCK, &start_ts) == 0) { 87 has_monotonic_time = SDL_TRUE; 88 } else 89 #elif defined(__APPLE__) 90 kern_return_t ret = mach_timebase_info(&mach_base_info); 91 if (ret == 0) { 92 has_monotonic_time = SDL_TRUE; 93 start_mach = mach_absolute_time(); 94 } else 95 #endif 96 { 97 gettimeofday(&start_tv, NULL); 98 } 99 } 100 101 void 102 SDL_TicksQuit(void) 103 { 104 ticks_started = SDL_FALSE; 105 } 106 107 Uint32 108 SDL_GetTicks(void) 109 { 110 Uint32 ticks; 111 if (!ticks_started) { 112 SDL_TicksInit(); 113 } 114 115 if (has_monotonic_time) { 116 #if HAVE_CLOCK_GETTIME 117 struct timespec now; 118 clock_gettime(SDL_MONOTONIC_CLOCK, &now); 119 ticks = (Uint32)((now.tv_sec - start_ts.tv_sec) * 1000 + (now.tv_nsec - start_ts.tv_nsec) / 1000000); 120 #elif defined(__APPLE__) 121 uint64_t now = mach_absolute_time(); 122 ticks = (Uint32)((((now - start_mach) * mach_base_info.numer) / mach_base_info.denom) / 1000000); 123 #else 124 SDL_assert(SDL_FALSE); 125 ticks = 0; 126 #endif 127 } else { 128 struct timeval now; 129 130 gettimeofday(&now, NULL); 131 ticks = (Uint32)((now.tv_sec - start_tv.tv_sec) * 1000 + (now.tv_usec - start_tv.tv_usec) / 1000); 132 } 133 return (ticks); 134 } 135 136 Uint64 137 SDL_GetPerformanceCounter(void) 138 { 139 Uint64 ticks; 140 if (!ticks_started) { 141 SDL_TicksInit(); 142 } 143 144 if (has_monotonic_time) { 145 #if HAVE_CLOCK_GETTIME 146 struct timespec now; 147 148 clock_gettime(SDL_MONOTONIC_CLOCK, &now); 149 ticks = now.tv_sec; 150 ticks *= 1000000000; 151 ticks += now.tv_nsec; 152 #elif defined(__APPLE__) 153 ticks = mach_absolute_time(); 154 #else 155 SDL_assert(SDL_FALSE); 156 ticks = 0; 157 #endif 158 } else { 159 struct timeval now; 160 161 gettimeofday(&now, NULL); 162 ticks = now.tv_sec; 163 ticks *= 1000000; 164 ticks += now.tv_usec; 165 } 166 return (ticks); 167 } 168 169 Uint64 170 SDL_GetPerformanceFrequency(void) 171 { 172 if (!ticks_started) { 173 SDL_TicksInit(); 174 } 175 176 if (has_monotonic_time) { 177 #if HAVE_CLOCK_GETTIME 178 return 1000000000; 179 #elif defined(__APPLE__) 180 Uint64 freq = mach_base_info.denom; 181 freq *= 1000000000; 182 freq /= mach_base_info.numer; 183 return freq; 184 #endif 185 } 186 187 return 1000000; 188 } 189 190 void 191 SDL_Delay(Uint32 ms) 192 { 193 #ifdef __EMSCRIPTEN__ 194 if (emscripten_has_asyncify() && SDL_GetHintBoolean(SDL_HINT_EMSCRIPTEN_ASYNCIFY, SDL_TRUE)) { 195 /* pseudo-synchronous pause, used directly or through e.g. SDL_WaitEvent */ 196 emscripten_sleep(ms); 197 return; 198 } 199 #endif 200 int was_error; 201 202 #if HAVE_NANOSLEEP 203 struct timespec elapsed, tv; 204 #else 205 struct timeval tv; 206 Uint32 then, now, elapsed; 207 #endif 208 209 /* Set the timeout interval */ 210 #if HAVE_NANOSLEEP 211 elapsed.tv_sec = ms / 1000; 212 elapsed.tv_nsec = (ms % 1000) * 1000000; 213 #else 214 then = SDL_GetTicks(); 215 #endif 216 do { 217 errno = 0; 218 219 #if HAVE_NANOSLEEP 220 tv.tv_sec = elapsed.tv_sec; 221 tv.tv_nsec = elapsed.tv_nsec; 222 was_error = nanosleep(&tv, &elapsed); 223 #else 224 /* Calculate the time interval left (in case of interrupt) */ 225 now = SDL_GetTicks(); 226 elapsed = (now - then); 227 then = now; 228 if (elapsed >= ms) { 229 break; 230 } 231 ms -= elapsed; 232 tv.tv_sec = ms / 1000; 233 tv.tv_usec = (ms % 1000) * 1000; 234 235 was_error = select(0, NULL, NULL, NULL, &tv); 236 #endif /* HAVE_NANOSLEEP */ 237 } while (was_error && (errno == EINTR)); 238 } 239 240 #endif /* SDL_TIMER_UNIX */ 241 242 /* vi: set ts=4 sw=4 expandtab: */