libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
timer.c
1#include "clod_thread_config.h"
2#include <clod/thread.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <errno.h>
6#include <string.h>
7#include <time.h>
8#include "yield.h"
9
10#if CLOD_HAVE_STDTHREADS
11#include <threads.h>
12#endif
13
14#define from_timespec(ts) ((ts).tv_sec * 1000 * 1000 + (ts).tv_nsec / 1000)
15#define to_timespec(us) ((struct timespec){.tv_sec = (us) / 1000000, .tv_nsec = (us) % 1000000 * 1000})
16struct timespec add_timespec(struct timespec ts, const int64_t us) {
17 ts.tv_sec += us / 1000000;
18 ts.tv_nsec += us % 1000000 * 1000;
19
20 while (ts.tv_nsec >= 1000000000) {
21 ts.tv_sec++;
22 ts.tv_nsec -= 1000000000;
23 }
24
25 while (ts.tv_nsec < 0) {
26 ts.tv_sec--;
27 ts.tv_nsec += 1000000000;
28 }
29
30 return ts;
31}
32
33#define FLAG_ABSOLUTE 1
34#define FLAG_CLOCK_GETTIME 2
35#define FLAG_TIMESPEC_GET 4
36#define FLAG_CLOCK_NANOSLEEP 8
37#define FLAG_NANOSLEEP 16
38#define FLAG_THREAD_SLEEP 32
39#define FLAG_MONOTONIC 128
40#define FLAG_MONOTONIC_RAW 256
41
42clod_once discover_mode_once = CLOD_ONCE_INIT;
43int mode = 0;
44
45void discover_mode() {
46 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_CLOCK_NANOSLEEP && defined(TIMER_ABSTIME)
47 do {
48 struct timespec time;
49 if (clock_gettime(CLOCK_MONOTONIC, &time) != 0)
50 break;
51
52 time = add_timespec(time, 10000);
53 if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &time, nullptr) != 0)
54 break;
55
56 mode = FLAG_MONOTONIC | FLAG_CLOCK_GETTIME | FLAG_CLOCK_NANOSLEEP | FLAG_ABSOLUTE;
57 return;
58 } while (0);
59 #endif
60
61 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_CLOCK_NANOSLEEP
62 do {
63 struct timespec time;
64 if (clock_gettime(CLOCK_MONOTONIC, &time) != 0)
65 break;
66
67 time = to_timespec(10000);
68 if (clock_nanosleep(CLOCK_MONOTONIC, 0, &time, nullptr) != 0)
69 break;
70
71 mode = FLAG_MONOTONIC | FLAG_CLOCK_GETTIME | FLAG_CLOCK_NANOSLEEP;
72 return;
73 } while (0);
74 #endif
75
76 #if defined(CLOCK_MONOTONIC_RAW) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_CLOCK_NANOSLEEP && defined(TIMER_ABSTIME)
77 do {
78 struct timespec time;
79 if (clock_gettime(CLOCK_MONOTONIC_RAW, &time) != 0)
80 break;
81
82 time = add_timespec(time, 10000);
83 if (clock_nanosleep(CLOCK_MONOTONIC_RAW, TIMER_ABSTIME, &time, nullptr) != 0)
84 break;
85
86 mode = FLAG_MONOTONIC_RAW | FLAG_CLOCK_GETTIME | FLAG_CLOCK_NANOSLEEP | FLAG_ABSOLUTE;
87 return;
88 } while (0);
89 #endif
90
91 #if defined(CLOCK_MONOTONIC_RAW) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_CLOCK_NANOSLEEP
92 do {
93 struct timespec time;
94 if (clock_gettime(CLOCK_MONOTONIC_RAW, &time) != 0)
95 break;
96
97 time = to_timespec(10000);
98 if (clock_nanosleep(CLOCK_MONOTONIC_RAW, 0, &time, nullptr) != 0)
99 break;
100
101 mode = FLAG_MONOTONIC_RAW | FLAG_CLOCK_GETTIME | FLAG_CLOCK_NANOSLEEP;
102 return;
103 } while (0);
104 #endif
105
106 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_NANOSLEEP
107 do {
108 struct timespec time;
109 if (clock_gettime(CLOCK_MONOTONIC, &time) != 0)
110 break;
111
112 time = to_timespec(10000);
113 if (nanosleep(&time, nullptr) != 0)
114 break;
115
116 mode = FLAG_MONOTONIC | FLAG_CLOCK_GETTIME | FLAG_NANOSLEEP;
117 return;
118 } while (0);
119 #endif
120
121 #if defined(CLOCK_MONOTONIC_RAW) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_NANOSLEEP
122 do {
123 struct timespec time;
124 if (clock_gettime(CLOCK_MONOTONIC_RAW, &time) != 0)
125 break;
126
127 time = to_timespec(10000);
128 if (nanosleep(&time, nullptr) != 0)
129 break;
130
131 mode = FLAG_MONOTONIC_RAW | FLAG_CLOCK_GETTIME | FLAG_NANOSLEEP;
132 return;
133 } while (0);
134 #endif
135
136 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_TIMESPEC_GET && CLOD_HAVE_NANOSLEEP
137 do {
138 struct timespec time;
139 if (timespec_get(&time, CLOCK_MONOTONIC) != CLOCK_MONOTONIC)
140 break;
141
142 time = to_timespec(10000);
143 if (nanosleep(&time, nullptr) != 0)
144 break;
145
146 mode = FLAG_MONOTONIC | FLAG_TIMESPEC_GET | FLAG_NANOSLEEP;
147 return;
148 } while (0);
149 #endif
150
151 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_TIMESPEC_GET && CLOD_HAVE_STDTHREADS
152 do {
153 struct timespec time;
154 if (timespec_get(&time, CLOCK_MONOTONIC) != CLOCK_MONOTONIC)
155 break;
156
157 time = to_timespec(10000);
158 if (thrd_sleep(&time, nullptr) != 0)
159 break;
160
161 mode = FLAG_MONOTONIC | FLAG_TIMESPEC_GET | FLAG_THREAD_SLEEP;
162 return;
163 } while (0);
164 #endif
165
166 fprintf(stderr, "libclod: no system clock methods worked on this system.");
167 exit(EXIT_FAILURE);
168}
169
170static int64_t now() {
171 CLOD_ONCE(&discover_mode_once)
172 discover_mode();
173
174 #if CLOD_HAVE_CLOCK_GETTIME
175 if (mode & FLAG_CLOCK_GETTIME) {
176
177 int clock_id = 0;
178 #if defined(CLOCK_MONOTONIC)
179 if (mode & FLAG_MONOTONIC) clock_id = CLOCK_MONOTONIC;
180 #endif
181 #if defined(CLOCK_MONOTONIC_RAW)
182 if (mode & FLAG_MONOTONIC_RAW) clock_id = CLOCK_MONOTONIC_RAW;
183 #endif
184
185 struct timespec time = {0};
186 if (clock_gettime(clock_id, &time) == 0)
187 return from_timespec(time);
188 }
189 #endif
190
191 #if CLOD_HAVE_TIMESPEC_GET
192 if (mode & FLAG_TIMESPEC_GET) {
193
194 int clock_id = 0;
195 #if defined(CLOCK_MONOTONIC)
196 if (mode & FLAG_MONOTONIC) clock_id = CLOCK_MONOTONIC;
197 #endif
198 #if defined(CLOCK_MONOTONIC_RAW)
199 if (mode & FLAG_MONOTONIC_RAW) clock_id = CLOCK_MONOTONIC_RAW;
200 #endif
201
202 struct timespec time = {0};
203 if (timespec_get(&time, clock_id) == clock_id)
204 return from_timespec(time);
205 }
206 #endif
207
208 #if CLOD_DEBUG_THREAD
209 fprintf(stderr, "libclod: failed to get system clock.");
210 #endif
211 return 0;
212}
213
214int64_t clod_timer(int64_t *time, const int64_t duration_us) {
215 int64_t enter = now();
216 if (duration_us <= 0) {
217 return enter;
218 }
219
220 int64_t end = enter + duration_us;
221 if (time) {
222 end = *time += duration_us;
223 if (end - enter < 0) {
224 return enter;
225 }
226 }
227
228 #if CLOD_HAVE_CLOCK_NANOSLEEP
229 if (mode & FLAG_CLOCK_NANOSLEEP) {
230 int clock = 0;
231 #if defined(CLOCK_MONOTONIC)
232 if (mode & FLAG_MONOTONIC) clock = CLOCK_MONOTONIC;
233 #endif
234 #if defined(CLOCK_MONOTONIC_RAW)
235 if (mode & FLAG_MONOTONIC_RAW) clock = CLOCK_MONOTONIC_RAW;
236 #endif
237
238 int flags = 0;
239 struct timespec wait_ts = to_timespec(end - enter);
240 #if defined(TIMER_ABSTIME)
241 if (mode & FLAG_ABSOLUTE) {
242 flags |= TIMER_ABSTIME;
243 wait_ts = to_timespec(end);
244 }
245 #endif
246
247 struct timespec remaining_ts;
248 while (1) {
249 int res = clock_nanosleep(clock, flags, &wait_ts, &remaining_ts);
250 if (res == 0) {
251 return enter;
252 }
253
254 if (res != EINTR) {
255 #if CLOD_DEBUG_THREAD
256 fprintf(stderr, "libclod: clock_nanosleep: %s\n", strerror(res));
257 #endif
258 if (time) *time -= duration_us;
259 return enter;
260 }
261
262 if (time) {
263 *time -= duration_us;
264 return enter;
265 }
266
267 if (!(mode & FLAG_ABSOLUTE)) {
268 wait_ts = remaining_ts;
269 }
270 };
271 }
272 #endif
273
274 #if CLOD_HAVE_NANOSLEEP
275 if (mode & FLAG_NANOSLEEP) {
276 struct timespec wait_ts = to_timespec(end - enter);
277 struct timespec remaining_ts;
278 while (1) {
279 int res = nanosleep(&wait_ts, &remaining_ts);
280 if (res == 0) {
281 return enter;
282 }
283
284 if (errno != EINTR) {
285 #if CLOD_DEBUG_THREAD
286 fprintf(stderr, "libclod: nanosleep: %s\n", strerror(errno));
287 #endif
288 if (time) *time -= duration_us;
289 return enter;
290 }
291
292 if (time) {
293 *time -= duration_us;
294 return enter;
295 }
296
297 wait_ts = remaining_ts;
298 };
299 }
300 #endif
301
302 #if CLOD_HAVE_STDTHREADS
303 if (mode & FLAG_THREAD_SLEEP) {
304 struct timespec wait_ts = to_timespec(end - enter);
305 struct timespec remaining_ts;
306 while (1) {
307 int res = thrd_sleep(&wait_ts, &remaining_ts);
308 if (res == 0) {
309 return enter;
310 }
311
312 if (errno != EINTR) {
313 #if CLOD_DEBUG_THREAD
314 fprintf(stderr, "libclod: thrd_sleep: %s\n", strerror(errno));
315 #endif
316 if (time) *time -= duration_us;
317 return enter;
318 }
319
320 if (time) {
321 *time -= duration_us;
322 return enter;
323 }
324
325 wait_ts = remaining_ts;
326 };
327 }
328 #endif
329
330 #if CLOD_DEBUG_THREAD
331 fprintf(stderr, "libclod: internal bug. discovered system clock methods don't exist.");
332 #endif
333 if (time) *time -= duration_us;
334 return enter;
335}
int64_t clod_timer(int64_t *time, const int64_t duration_us)
Definition timer.c:214
char clod_once
Definition thread.h:141
Sized string helpers.