1#include "clod_thread_config.h"
10#if CLOD_HAVE_STDTHREADS
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;
20 while (ts.tv_nsec >= 1000000000) {
22 ts.tv_nsec -= 1000000000;
25 while (ts.tv_nsec < 0) {
27 ts.tv_nsec += 1000000000;
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
42clod_once discover_mode_once = CLOD_ONCE_INIT;
46 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_CLOCK_NANOSLEEP && defined(TIMER_ABSTIME)
49 if (clock_gettime(CLOCK_MONOTONIC, &time) != 0)
52 time = add_timespec(time, 10000);
53 if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &time,
nullptr) != 0)
56 mode = FLAG_MONOTONIC | FLAG_CLOCK_GETTIME | FLAG_CLOCK_NANOSLEEP | FLAG_ABSOLUTE;
61 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_CLOCK_NANOSLEEP
64 if (clock_gettime(CLOCK_MONOTONIC, &time) != 0)
67 time = to_timespec(10000);
68 if (clock_nanosleep(CLOCK_MONOTONIC, 0, &time,
nullptr) != 0)
71 mode = FLAG_MONOTONIC | FLAG_CLOCK_GETTIME | FLAG_CLOCK_NANOSLEEP;
76 #if defined(CLOCK_MONOTONIC_RAW) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_CLOCK_NANOSLEEP && defined(TIMER_ABSTIME)
79 if (clock_gettime(CLOCK_MONOTONIC_RAW, &time) != 0)
82 time = add_timespec(time, 10000);
83 if (clock_nanosleep(CLOCK_MONOTONIC_RAW, TIMER_ABSTIME, &time,
nullptr) != 0)
86 mode = FLAG_MONOTONIC_RAW | FLAG_CLOCK_GETTIME | FLAG_CLOCK_NANOSLEEP | FLAG_ABSOLUTE;
91 #if defined(CLOCK_MONOTONIC_RAW) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_CLOCK_NANOSLEEP
94 if (clock_gettime(CLOCK_MONOTONIC_RAW, &time) != 0)
97 time = to_timespec(10000);
98 if (clock_nanosleep(CLOCK_MONOTONIC_RAW, 0, &time,
nullptr) != 0)
101 mode = FLAG_MONOTONIC_RAW | FLAG_CLOCK_GETTIME | FLAG_CLOCK_NANOSLEEP;
106 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_NANOSLEEP
108 struct timespec time;
109 if (clock_gettime(CLOCK_MONOTONIC, &time) != 0)
112 time = to_timespec(10000);
113 if (nanosleep(&time,
nullptr) != 0)
116 mode = FLAG_MONOTONIC | FLAG_CLOCK_GETTIME | FLAG_NANOSLEEP;
121 #if defined(CLOCK_MONOTONIC_RAW) && CLOD_HAVE_CLOCK_GETTIME && CLOD_HAVE_NANOSLEEP
123 struct timespec time;
124 if (clock_gettime(CLOCK_MONOTONIC_RAW, &time) != 0)
127 time = to_timespec(10000);
128 if (nanosleep(&time,
nullptr) != 0)
131 mode = FLAG_MONOTONIC_RAW | FLAG_CLOCK_GETTIME | FLAG_NANOSLEEP;
136 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_TIMESPEC_GET && CLOD_HAVE_NANOSLEEP
138 struct timespec time;
139 if (timespec_get(&time, CLOCK_MONOTONIC) != CLOCK_MONOTONIC)
142 time = to_timespec(10000);
143 if (nanosleep(&time,
nullptr) != 0)
146 mode = FLAG_MONOTONIC | FLAG_TIMESPEC_GET | FLAG_NANOSLEEP;
151 #if defined(CLOCK_MONOTONIC) && CLOD_HAVE_TIMESPEC_GET && CLOD_HAVE_STDTHREADS
153 struct timespec time;
154 if (timespec_get(&time, CLOCK_MONOTONIC) != CLOCK_MONOTONIC)
157 time = to_timespec(10000);
158 if (thrd_sleep(&time,
nullptr) != 0)
161 mode = FLAG_MONOTONIC | FLAG_TIMESPEC_GET | FLAG_THREAD_SLEEP;
166 fprintf(stderr,
"libclod: no system clock methods worked on this system.");
170static int64_t now() {
171 CLOD_ONCE(&discover_mode_once)
174 #if CLOD_HAVE_CLOCK_GETTIME
175 if (mode & FLAG_CLOCK_GETTIME) {
178 #if defined(CLOCK_MONOTONIC)
179 if (mode & FLAG_MONOTONIC) clock_id = CLOCK_MONOTONIC;
181 #if defined(CLOCK_MONOTONIC_RAW)
182 if (mode & FLAG_MONOTONIC_RAW) clock_id = CLOCK_MONOTONIC_RAW;
185 struct timespec time = {0};
186 if (clock_gettime(clock_id, &time) == 0)
187 return from_timespec(time);
191 #if CLOD_HAVE_TIMESPEC_GET
192 if (mode & FLAG_TIMESPEC_GET) {
195 #if defined(CLOCK_MONOTONIC)
196 if (mode & FLAG_MONOTONIC) clock_id = CLOCK_MONOTONIC;
198 #if defined(CLOCK_MONOTONIC_RAW)
199 if (mode & FLAG_MONOTONIC_RAW) clock_id = CLOCK_MONOTONIC_RAW;
202 struct timespec time = {0};
203 if (timespec_get(&time, clock_id) == clock_id)
204 return from_timespec(time);
208 #if CLOD_DEBUG_THREAD
209 fprintf(stderr,
"libclod: failed to get system clock.");
214int64_t
clod_timer(int64_t *time,
const int64_t duration_us) {
215 int64_t enter = now();
216 if (duration_us <= 0) {
220 int64_t end = enter + duration_us;
222 end = *time += duration_us;
223 if (end - enter < 0) {
228 #if CLOD_HAVE_CLOCK_NANOSLEEP
229 if (mode & FLAG_CLOCK_NANOSLEEP) {
231 #if defined(CLOCK_MONOTONIC)
232 if (mode & FLAG_MONOTONIC) clock = CLOCK_MONOTONIC;
234 #if defined(CLOCK_MONOTONIC_RAW)
235 if (mode & FLAG_MONOTONIC_RAW) clock = CLOCK_MONOTONIC_RAW;
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);
247 struct timespec remaining_ts;
249 int res = clock_nanosleep(clock, flags, &wait_ts, &remaining_ts);
255 #if CLOD_DEBUG_THREAD
256 fprintf(stderr,
"libclod: clock_nanosleep: %s\n", strerror(res));
258 if (time) *time -= duration_us;
263 *time -= duration_us;
267 if (!(mode & FLAG_ABSOLUTE)) {
268 wait_ts = remaining_ts;
274 #if CLOD_HAVE_NANOSLEEP
275 if (mode & FLAG_NANOSLEEP) {
276 struct timespec wait_ts = to_timespec(end - enter);
277 struct timespec remaining_ts;
279 int res = nanosleep(&wait_ts, &remaining_ts);
284 if (errno != EINTR) {
285 #if CLOD_DEBUG_THREAD
286 fprintf(stderr,
"libclod: nanosleep: %s\n", strerror(errno));
288 if (time) *time -= duration_us;
293 *time -= duration_us;
297 wait_ts = remaining_ts;
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;
307 int res = thrd_sleep(&wait_ts, &remaining_ts);
312 if (errno != EINTR) {
313 #if CLOD_DEBUG_THREAD
314 fprintf(stderr,
"libclod: thrd_sleep: %s\n", strerror(errno));
316 if (time) *time -= duration_us;
321 *time -= duration_us;
325 wait_ts = remaining_ts;
330 #if CLOD_DEBUG_THREAD
331 fprintf(stderr,
"libclod: internal bug. discovered system clock methods don't exist.");
333 if (time) *time -= duration_us;
int64_t clod_timer(int64_t *time, const int64_t duration_us)