1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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: */ |
243 | |