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_assert.h"
32 #include "SDL_hints.h"
33 #include "../SDL_timer_c.h"
34
35 #ifdef __EMSCRIPTEN__
36 #include <emscripten.h>
37 #endif
38
39 /* The clock_gettime provides monotonous time, so we should use it if
40 it's available. The clock_gettime function is behind ifdef
41 for __USE_POSIX199309
42 Tommi Kyntola (tommi.kyntola@ray.fi) 27/09/2005
43 */
44 /* Reworked monotonic clock to not assume the current system has one
45 as not all linux kernels provide a monotonic clock (yeah recent ones
46 probably do)
47 Also added OS X Monotonic clock support
48 Based on work in https://github.com/ThomasHabets/monotonic_clock
49 */
50 #if HAVE_NANOSLEEP || HAVE_CLOCK_GETTIME
51 #include <time.h>
52 #endif
53 #ifdef __APPLE__
54 #include <mach/mach_time.h>
55 #endif
56
57 /* Use CLOCK_MONOTONIC_RAW, if available, which is not subject to adjustment by NTP */
58 #if HAVE_CLOCK_GETTIME
59 #ifdef CLOCK_MONOTONIC_RAW
60 #define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC_RAW
61 #else
62 #define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC
63 #endif
64 #endif
65
66 /* The first ticks value of the application */
67 #if HAVE_CLOCK_GETTIME
68 static struct timespec start_ts;
69 #elif defined(__APPLE__)
70 static uint64_t start_mach;
71 mach_timebase_info_data_t mach_base_info;
72 #endif
73 static SDL_bool has_monotonic_time = SDL_FALSE;
74 static struct timeval start_tv;
75 static SDL_bool ticks_started = SDL_FALSE;
76
77 void
SDL_TicksInit(void)78 SDL_TicksInit(void)
79 {
80 if (ticks_started) {
81 return;
82 }
83 ticks_started = SDL_TRUE;
84
85 /* Set first ticks value */
86 #if HAVE_CLOCK_GETTIME
87 if (clock_gettime(SDL_MONOTONIC_CLOCK, &start_ts) == 0) {
88 has_monotonic_time = SDL_TRUE;
89 } else
90 #elif defined(__APPLE__)
91 kern_return_t ret = mach_timebase_info(&mach_base_info);
92 if (ret == 0) {
93 has_monotonic_time = SDL_TRUE;
94 start_mach = mach_absolute_time();
95 } else
96 #endif
97 {
98 gettimeofday(&start_tv, NULL);
99 }
100 }
101
102 void
SDL_TicksQuit(void)103 SDL_TicksQuit(void)
104 {
105 ticks_started = SDL_FALSE;
106 }
107
108 Uint32
SDL_GetTicks(void)109 SDL_GetTicks(void)
110 {
111 Uint32 ticks;
112 if (!ticks_started) {
113 SDL_TicksInit();
114 }
115
116 if (has_monotonic_time) {
117 #if HAVE_CLOCK_GETTIME
118 struct timespec now;
119 clock_gettime(SDL_MONOTONIC_CLOCK, &now);
120 ticks = (Uint32)((now.tv_sec - start_ts.tv_sec) * 1000 + (now.tv_nsec - start_ts.tv_nsec) / 1000000);
121 #elif defined(__APPLE__)
122 uint64_t now = mach_absolute_time();
123 ticks = (Uint32)((((now - start_mach) * mach_base_info.numer) / mach_base_info.denom) / 1000000);
124 #else
125 SDL_assert(SDL_FALSE);
126 ticks = 0;
127 #endif
128 } else {
129 struct timeval now;
130
131 gettimeofday(&now, NULL);
132 ticks = (Uint32)((now.tv_sec - start_tv.tv_sec) * 1000 + (now.tv_usec - start_tv.tv_usec) / 1000);
133 }
134 return (ticks);
135 }
136
137 Uint64
SDL_GetPerformanceCounter(void)138 SDL_GetPerformanceCounter(void)
139 {
140 Uint64 ticks;
141 if (!ticks_started) {
142 SDL_TicksInit();
143 }
144
145 if (has_monotonic_time) {
146 #if HAVE_CLOCK_GETTIME
147 struct timespec now;
148
149 clock_gettime(SDL_MONOTONIC_CLOCK, &now);
150 ticks = now.tv_sec;
151 ticks *= 1000000000;
152 ticks += now.tv_nsec;
153 #elif defined(__APPLE__)
154 ticks = mach_absolute_time();
155 #else
156 SDL_assert(SDL_FALSE);
157 ticks = 0;
158 #endif
159 } else {
160 struct timeval now;
161
162 gettimeofday(&now, NULL);
163 ticks = now.tv_sec;
164 ticks *= 1000000;
165 ticks += now.tv_usec;
166 }
167 return (ticks);
168 }
169
170 Uint64
SDL_GetPerformanceFrequency(void)171 SDL_GetPerformanceFrequency(void)
172 {
173 if (!ticks_started) {
174 SDL_TicksInit();
175 }
176
177 if (has_monotonic_time) {
178 #if HAVE_CLOCK_GETTIME
179 return 1000000000;
180 #elif defined(__APPLE__)
181 Uint64 freq = mach_base_info.denom;
182 freq *= 1000000000;
183 freq /= mach_base_info.numer;
184 return freq;
185 #endif
186 }
187
188 return 1000000;
189 }
190
191 void
SDL_Delay(Uint32 ms)192 SDL_Delay(Uint32 ms)
193 {
194 #ifdef __EMSCRIPTEN__
195 if (emscripten_has_asyncify() && SDL_GetHintBoolean(SDL_HINT_EMSCRIPTEN_ASYNCIFY, SDL_TRUE)) {
196 /* pseudo-synchronous pause, used directly or through e.g. SDL_WaitEvent */
197 emscripten_sleep(ms);
198 return;
199 }
200 #endif
201 int was_error;
202
203 #if HAVE_NANOSLEEP
204 struct timespec elapsed, tv;
205 #else
206 struct timeval tv;
207 Uint32 then, now, elapsed;
208 #endif
209
210 /* Set the timeout interval */
211 #if HAVE_NANOSLEEP
212 elapsed.tv_sec = ms / 1000;
213 elapsed.tv_nsec = (ms % 1000) * 1000000;
214 #else
215 then = SDL_GetTicks();
216 #endif
217 do {
218 errno = 0;
219
220 #if HAVE_NANOSLEEP
221 tv.tv_sec = elapsed.tv_sec;
222 tv.tv_nsec = elapsed.tv_nsec;
223 was_error = nanosleep(&tv, &elapsed);
224 #else
225 /* Calculate the time interval left (in case of interrupt) */
226 now = SDL_GetTicks();
227 elapsed = (now - then);
228 then = now;
229 if (elapsed >= ms) {
230 break;
231 }
232 ms -= elapsed;
233 tv.tv_sec = ms / 1000;
234 tv.tv_usec = (ms % 1000) * 1000;
235
236 was_error = select(0, NULL, NULL, NULL, &tv);
237 #endif /* HAVE_NANOSLEEP */
238 } while (was_error && (errno == EINTR));
239 }
240
241 #endif /* SDL_TIMER_UNIX */
242
243 /* vi: set ts=4 sw=4 expandtab: */
244