1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 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_c.h" |
31 | |
32 | #ifdef SDL_PLATFORM_EMSCRIPTEN |
33 | #include <emscripten.h> |
34 | #endif |
35 | |
36 | /* The clock_gettime provides monotonous time, so we should use it if |
37 | it's available. The clock_gettime function is behind ifdef |
38 | for __USE_POSIX199309 |
39 | Tommi Kyntola (tommi.kyntola@ray.fi) 27/09/2005 |
40 | */ |
41 | /* Reworked monotonic clock to not assume the current system has one |
42 | as not all linux kernels provide a monotonic clock (yeah recent ones |
43 | probably do) |
44 | Also added macOS Monotonic clock support |
45 | Based on work in https://github.com/ThomasHabets/monotonic_clock |
46 | */ |
47 | #if defined(HAVE_NANOSLEEP) || defined(HAVE_CLOCK_GETTIME) |
48 | #include <time.h> |
49 | #endif |
50 | #ifdef SDL_PLATFORM_APPLE |
51 | #include <mach/mach_time.h> |
52 | #endif |
53 | |
54 | // Use CLOCK_MONOTONIC_RAW, if available, which is not subject to adjustment by NTP |
55 | #ifdef HAVE_CLOCK_GETTIME |
56 | #ifdef CLOCK_MONOTONIC_RAW |
57 | #define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC_RAW |
58 | #else |
59 | #define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC |
60 | #endif |
61 | #endif |
62 | |
63 | // The first ticks value of the application |
64 | #if !defined(HAVE_CLOCK_GETTIME) && defined(SDL_PLATFORM_APPLE) |
65 | mach_timebase_info_data_t mach_base_info; |
66 | #endif |
67 | static bool checked_monotonic_time = false; |
68 | static bool has_monotonic_time = false; |
69 | |
70 | static void CheckMonotonicTime(void) |
71 | { |
72 | #ifdef HAVE_CLOCK_GETTIME |
73 | struct timespec value; |
74 | if (clock_gettime(SDL_MONOTONIC_CLOCK, &value) == 0) { |
75 | has_monotonic_time = true; |
76 | } |
77 | #elif defined(SDL_PLATFORM_APPLE) |
78 | if (mach_timebase_info(&mach_base_info) == 0) { |
79 | has_monotonic_time = true; |
80 | } |
81 | #endif |
82 | checked_monotonic_time = true; |
83 | } |
84 | |
85 | Uint64 SDL_GetPerformanceCounter(void) |
86 | { |
87 | Uint64 ticks; |
88 | |
89 | if (!checked_monotonic_time) { |
90 | CheckMonotonicTime(); |
91 | } |
92 | |
93 | if (has_monotonic_time) { |
94 | #ifdef HAVE_CLOCK_GETTIME |
95 | struct timespec now; |
96 | |
97 | clock_gettime(SDL_MONOTONIC_CLOCK, &now); |
98 | ticks = now.tv_sec; |
99 | ticks *= SDL_NS_PER_SECOND; |
100 | ticks += now.tv_nsec; |
101 | #elif defined(SDL_PLATFORM_APPLE) |
102 | ticks = mach_absolute_time(); |
103 | #else |
104 | SDL_assert(false); |
105 | ticks = 0; |
106 | #endif |
107 | } else { |
108 | struct timeval now; |
109 | |
110 | gettimeofday(&now, NULL); |
111 | ticks = now.tv_sec; |
112 | ticks *= SDL_US_PER_SECOND; |
113 | ticks += now.tv_usec; |
114 | } |
115 | return ticks; |
116 | } |
117 | |
118 | Uint64 SDL_GetPerformanceFrequency(void) |
119 | { |
120 | if (!checked_monotonic_time) { |
121 | CheckMonotonicTime(); |
122 | } |
123 | |
124 | if (has_monotonic_time) { |
125 | #ifdef HAVE_CLOCK_GETTIME |
126 | return SDL_NS_PER_SECOND; |
127 | #elif defined(SDL_PLATFORM_APPLE) |
128 | Uint64 freq = mach_base_info.denom; |
129 | freq *= SDL_NS_PER_SECOND; |
130 | freq /= mach_base_info.numer; |
131 | return freq; |
132 | #endif |
133 | } |
134 | |
135 | return SDL_US_PER_SECOND; |
136 | } |
137 | |
138 | void SDL_SYS_DelayNS(Uint64 ns) |
139 | { |
140 | int was_error; |
141 | |
142 | #ifdef HAVE_NANOSLEEP |
143 | struct timespec tv, remaining; |
144 | #else |
145 | struct timeval tv; |
146 | Uint64 then, now, elapsed; |
147 | #endif |
148 | |
149 | #ifdef SDL_PLATFORM_EMSCRIPTEN |
150 | if (emscripten_has_asyncify() && SDL_GetHintBoolean(SDL_HINT_EMSCRIPTEN_ASYNCIFY, true)) { |
151 | // pseudo-synchronous pause, used directly or through e.g. SDL_WaitEvent |
152 | emscripten_sleep(ns / SDL_NS_PER_MS); |
153 | return; |
154 | } |
155 | #endif |
156 | |
157 | // Set the timeout interval |
158 | #ifdef HAVE_NANOSLEEP |
159 | remaining.tv_sec = (time_t)(ns / SDL_NS_PER_SECOND); |
160 | remaining.tv_nsec = (long)(ns % SDL_NS_PER_SECOND); |
161 | #else |
162 | then = SDL_GetTicksNS(); |
163 | #endif |
164 | do { |
165 | errno = 0; |
166 | |
167 | #ifdef HAVE_NANOSLEEP |
168 | tv.tv_sec = remaining.tv_sec; |
169 | tv.tv_nsec = remaining.tv_nsec; |
170 | was_error = nanosleep(&tv, &remaining); |
171 | #else |
172 | // Calculate the time interval left (in case of interrupt) |
173 | now = SDL_GetTicksNS(); |
174 | elapsed = (now - then); |
175 | then = now; |
176 | if (elapsed >= ns) { |
177 | break; |
178 | } |
179 | ns -= elapsed; |
180 | tv.tv_sec = (ns / SDL_NS_PER_SECOND); |
181 | tv.tv_usec = SDL_NS_TO_US(ns % SDL_NS_PER_SECOND); |
182 | |
183 | was_error = select(0, NULL, NULL, NULL, &tv); |
184 | #endif // HAVE_NANOSLEEP |
185 | } while (was_error && (errno == EINTR)); |
186 | } |
187 | |
188 | #endif // SDL_TIMER_UNIX |
189 | |